将.class自己码转化为.dex字节码作为Apk打包的关键步骤,Google打算在Android 3.0中引入D8作为原先Dex的升级版,以及R8作为原本Proguard 压缩与优化(minification、shrinking、optimization)部分的替代品。升级Dex编译器将直接影响构建时间,.dex文件大小,运行时性能。
谷歌通过自己的 基准测试项目测出,编译时间缩短了20%,而且.dex文件更小,虽然只有几个百分比。D8编译的.dex文件将拥有相同或者是更好的运行时性能。
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 支持。
目前Java 8语言支持的处理是在javac之后,与字节码处理工具处理之前。在接下来的几个月,这个步骤将会被移动到pipeline的后一个阶段,作为D8的一部分。
已经在Android Studio 3.0 Beta release中引入
android.enableD8=true
android.enableD8=false
android.enableD8.desugaring = false
当我们选择JDK8以上版本时,有时候会使用lambda表达式,在设置android.enableD8.desugaring = false
的时候。编译链会对lambda表达式进行一次脱糖处理。请看下面的例子。
一个简单的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);
//lambda写法
tv.setOnClickListener(view -> {
Log.d("MainActivity", "MainActivity");
});
//普通写法
tv.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) {
Log.d("MainActivity", "MainActivity");
}
});
}
}
路径为
app/build/intermediates/transforms/desugar/release(buildType)/0/com.jamin.d8desugar(packageName)/
public class MainActivity extends Activity {
public MainActivity() {
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(2130968576);
TextView tv = (TextView)this.findViewById(2130903040);
//直接使用Lambda表达式脱糖后的静态引用
tv.setOnClickListener(MainActivity$$Lambda$0.$instance);
tv.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
Log.d("MainActivity", "MainActivity");
}
});
}
}
//Class文件中生成静态引用
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的语法糖是无法识别的。
好,我们简单改写一下源文件
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);
}
});
}
}
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文件就不是纯函数了。所以会不会内存泄漏?
在设置android.enableD8.desugaring = true
的时候(高版本,比如AGP的版本是com.android.tools.build:gradle:3.3.0-alpha03时,默认是D8脱糖),D8脱糖就不会在transforms目录下生成desugar目录。反编译transforms/dexBuilder/中的jar包。可以看到在jar包中,已经是脱糖后的结果了。大家可以看下图。也是把lambda表达式生成一个静态对象。
当然D8脱糖,要求编译链中所有工具都支持java8,不然不认识class文件中的部分语法糖。D8脱糖的好处是什么呢。官方的话说就是可以提高编译速度。
R8作为原本Proguard 压缩与优化(minification、shrinking、optimization)部分的替代品,依然使用与Proguard一样的keep规则。
目前R8已经开源: r8/r8,其包含了D8与R8。
目前R8还没有整合进Android Gradle plugin,不过由于其已经开源,根据文档可以很快的在python环境下运行起来:
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