Android Studio 3.0+ 新Dex编译器D8 Desugar R8

〇.序

将.class自己码转化为.dex字节码作为Apk打包的关键步骤,Google打算在Android 3.0中引入D8作为原先Dex的升级版,以及R8作为原本Proguard 压缩与优化(minification、shrinking、optimization)部分的替代品。升级Dex编译器将直接影响构建时间,.dex文件大小,运行时性能。

一.D8

1.1 D8 的功能是把java字节码转化成dex代码,D8作为DX的一个替换方案。

谷歌通过自己的 基准测试项目测出,编译时间缩短了20%,而且.dex文件更小,虽然只有几个百分比。D8编译的.dex文件将拥有相同或者是更好的运行时性能。

Dex编译时间 DX VS D8
dex文件大小 DX VS D8

Java 8支持相关

Android Studio 3.0 及以上版本支持所有 Java 7 语言功能,以及部分 Java 8 语言功能(具体因平台版本而异)。
注:在开发 Android 应用时,可以选择使用 Java 8 语言功能。 您可以将项目的源代码和目标代码兼容性值保留为 Java 7,但仍须使用 JDK 8 进行编译。
Android Studio 为使用部分 Java 8 语言功能及利用这些功能的第三方库提供内置支持。 如图 1 所示,默认工具链对 javac 编译器的输出执行字节码转换(称为 desugar),从而实现新语言功能。 Jack 不再受支持,您需要首先停用 Jack 才能使用默认工具链内置的 Java 8 支持。

采用 desugar 字节码转换的 Java 8 语言功能支持。

目前Java 8语言支持的处理是在javac之后,与字节码处理工具处理之前。在接下来的几个月,这个步骤将会被移动到pipeline的后一个阶段,作为D8的一部分。

其带来的影响:

  • 减少这块的编译时间
  • 可以优化更多代码
  • 这么一来,所有字节码处理工具就必须要支持Java8的字节码格式了。

1.2 D8的使用

已经在Android Studio 3.0 Beta release中引入

  • Android Studio 3.0
    需要主动在gradle.properties文件中新增:android.enableD8=true
  • Android Studio 3.1或之后的版本
    在3.1或之后的版本D8将会被作为默认的Dex编译器。如果遇到问题,你可以通过修改gradle.properties文件里的一个属性恢复到DX android.enableD8=false
  • 除了其他好处外,使用D8还有一个好处,就是支持 脱糖,让Java 8才提供的特性(如lambdas)可以转换成Java 7特性。把脱糖步骤集成进D8影响了所有读或写.class字节码的开发工具,因为它会使用Java 8格式。你可以在gradle文件中设置一个属性,恢复到以前的行为,让脱糖发生在Java编译之后,.class字节码仍遵循Java 7格式:android.enableD8.desugaring = true
D8 dexer

d8 dexer desuger

1.3脱糖(Desugar)

当我们选择JDK8以上版本时,有时候会使用lambda表达式,在设置android.enableD8.desugaring = false的时候。编译链会对lambda表达式进行一次脱糖处理。请看下面的例子。

1.3.1 纯函数脱糖

源代码很简单:

一个简单的Activity,设置ClickListener一种是Java7以下的传统写法,一种是Java8的Lambda表达式写法

public class MainActivity extends Activity {

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    TextView tv = findViewById(R.id.click);
    tv.setOnClickListener(view -> {
      Log.d("MainActivity", "MainActivity");
    });
    tv.setOnClickListener(new View.OnClickListener() {
      @Override public void onClick(View view) {
        Log.d("MainActivity", "MainActivity");
      }
    });

  }
}

编译后的Class文件如下:

路径为
app/build/intermediates/transforms/desugar/release(buildType)/0/com.jamin.d8desugar(packageName)/

desugar生成的class放置的位置

public class MainActivity extends Activity {

  public MainActivity() {
  }

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    this.setContentView(2130968576);
    TextView tv = (TextView)this.findViewById(2130903040);
    tv.setOnClickListener(MainActivity$$Lambda$0.$instance);
    tv.setOnClickListener(new OnClickListener() {
      public void onClick(View view) {
        Log.d("MainActivity", "MainActivity");
      }
    });
  }
}
final class MainActivity$$Lambda$0 implements OnClickListener {
  static final OnClickListener $instance = new MainActivity$$Lambda$0();

  private MainActivity$$Lambda$0() {
  }

  public void onClick(View var1) {
    MainActivity.lambda$onCreate$0$MainActivity(var1);
  }
}

实际上非D8脱糖,为了保证JAVA7及以下的兼容性。是将lambda表达式的在javac编译class的时候就已经将lambda表达式转化成更高兼容度的低版本代码。好处是在编译链中,有时候会使用一些java7的工具。他们对于java8的语法糖是无法识别的。

1.3.2 非纯函数脱糖

好,我们简单改写一下源文件

public class MainActivity extends Activity {

  String abc = "abc";

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    TextView tv = findViewById(R.id.click);
    tv.setOnClickListener(view -> {
      Log.d("MainActivity", "MainActivity" + abc);
    });
    tv.setOnClickListener(new View.OnClickListener() {
      @Override public void onClick(View view) {
        Log.d("MainActivity", "MainActivity" + abc);
      }
    });
  }
}
生成的class文件如下:
public class MainActivity extends Activity {
  String abc = "abc";

  public MainActivity() {
  }

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    this.setContentView(2130968576);
    TextView tv = (TextView)this.findViewById(2130903040);
    //注意this传递过去了。类似于内部类的写法
    tv.setOnClickListener(new MainActivity$$Lambda$0(this));
    tv.setOnClickListener(new OnClickListener() {
      public void onClick(View view) {
        Log.d("MainActivity", "MainActivity" + MainActivity.this.abc);
      }
    });
  }
}
// $FF: synthetic class
final class MainActivity$$Lambda$0 implements OnClickListener {
  private final MainActivity arg$1;

  MainActivity$$Lambda$0(MainActivity var1) {
    this.arg$1 = var1;
  }

  public void onClick(View var1) {
    this.arg$1.lambda$onCreate$0$MainActivity(var1);
  }
}

此时生成的class文件就不是纯函数了。所以会不会内存泄漏?

1.3.3 D8脱糖

在设置android.enableD8.desugaring = true的时候(高版本,比如AGP的版本是com.android.tools.build:gradle:3.3.0-alpha03时,默认是D8脱糖),D8脱糖就不会在transforms目录下生成desugar目录。反编译transforms/dexBuilder/中的jar包。可以看到在jar包中,已经是脱糖后的结果了。大家可以看下图。也是把lambda表达式生成一个静态对象。

MainActivity

Lambda

当然D8脱糖,要求编译链中所有工具都支持java8,不然不认识class文件中的部分语法糖。D8脱糖的好处是什么呢。官方的话说就是可以提高编译速度。

二.R8

R8作为原本Proguard 压缩与优化(minification、shrinking、optimization)部分的替代品,依然使用与Proguard一样的keep规则。
目前R8已经开源: r8/r8,其包含了D8与R8。

目前R8还没有整合进Android Gradle plugin,不过由于其已经开源,根据文档可以很快的在python环境下运行起来:

  1. 确保本地已经安装了python 2.7或更高版本(macOS Sierra自带python 2.7)。
  2. 由于R8项目使用chromium项目提供的depot_tools管理依赖,因此先安装depot_tools
  3. Clone R8项目:git clone https://r8.googlesource.com/r8 && cd r8
  4. 下载一个Gradle版去编译,并且声称两个jar文件: build/libs/d8.jar与build/libs/r8.jar: python tools/gradle.py d8 r8
    根据r8文档进行使用即可

BREAKING NEWS:新版AndroidStudio可以体验一下。

New code shrinker R8 is a new tool for code shrinking and obfuscation that replaces ProGuard. You can start using the preview version of R8 by including the following in your project’s gradle.properties file: android.enableR8 = true
官方文档: New code shrinker

参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • Android 新一代编译 toolchain Jack & Jill 简介 2016 年 3 月 10 日, G...
    heiheiwanne阅读 1,149评论 0 3
  • 我叫cimi,是人类社会为了区别我们,给予物质已命名已概念,对于我那个符号是:cimi;橘猫;豆芽的情人受;大力...
    feono阅读 578评论 1 6
  • 时光如梭尘路不平,大多数人的每时每刻都在为生存而忙忙碌碌的奔波着,为生活而辛辛苦苦的操持着。 尽管在现实状况里很多...
    大浪淘沙S6阅读 275评论 0 1
  • “你买这么大的东西之前考虑过三轮车的感受吗?”---今天一早我被快递小哥在电话那头这样问到。 “那你送不了货的话,...
    杂食的明子阅读 587评论 0 0