一. 背景
android studio 3.0 (一下简称as3)已经发布一段时间了. 现在最新的as版本已经到了3.1了 !
as3有许多新特性, 如: 支持Kotlin, 支持Java8 等.
as3.0之前想用lambda表达式, 你得用Retrolambda这个库来进行兼容.
比较坑的是在我们的安卓项目中有些地方使用lambda会导致编译错误, 而另一些地方则正常! 因此我想干掉retrolambda直接使用as3对java8的支持. 下面说一下如何在as3中使用java8以及其大致原理.
二. 在as3中使用Java8
- 配置compileOptions为java8 且 禁止Jack:
https://developer.android.com/studio/write/java8-support.html#disable_jack
android {
...
defaultConfig {
...
// Remove this block.
jackOptions {
enabled true
...
}
}
// Keep the following configuration in order to target Java 8.
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
经过上述配置就可以使用lambda表达式了, 具体可以使用哪些java8的特性 (Java8的许多特性需要API24才能用), 请敲击此处: https://developer.android.com/studio/write/java8-support.html
三. 原理
-
为了更好的支持动态语言(Groovy、JRuby、Jython、Scala等)Java7增加了新的调用指令invokedynamic (JSR 292). 此指令后来用到了Java8 lambda表达式的实现上 (JSR 335). 然而Dalvik/Art虚拟机并不认识
invokedynamic
指令, 因此google自己搞了一个叫desugar的东西来实现与invokedynamic
指令类似的功能. -
在java8中使用lambda表达式时, 是直接生成一条
invokedynamic
指令来实现动态调用的.
下面是java8中的一个lambda表达式的案例:
public class Test {
public static void main(String[] args) {
new Thread(() -> System.out.println("Hello world !")).start();
}
}
编译之后, 使用javap -c Test
反编译得到如下信息:
可以看到框住的部分就是
invokedynmaic
指令实现的lambda表达式调用, 这里并没有创建一个Runnable
匿名类.
- 再来看看as3中的lambda表达式的调用
下面是lambda表达式调用的源码:
package com.stone.app;
public class Test {
static void test() {
new Thread(() -> System.out.println("Hello world !")).start();
}
}
使用jadx查看到如下信息:
反编译的Test
类内容如下:
package com.stone.app;
public class Test {
static void test() {
new Thread(Test$$Lambda$0.$instance).start();
}
}
可以看到确实生成了一个类Test$$Lambda$0
用来表示lambda表达式. Test$$Lambda$0
类反编译后内容如下:
package com.stone.app;
final /* synthetic */ class Test$$Lambda$0 implements Runnable {
static final Runnable $instance = new Test$$Lambda$0();
private Test$$Lambda$0() {
}
public void run() {
System.out.println("Hello world !");
}
}
虽然生成的这个代表lambda表达式的类是 final且近似于单例, 但毕竟生成了类, 这将导致额为调用和存储的开销. 此外, 如果在lambda表达式中调用外部类的东西, 还有可能导致内存泄漏的问题. 因此使用lambda表达式时必须谨慎 !
AS3.0的desugar机制虽然让我们可以使用java8的lambda特性, 但是性能肯定不如invokedynamic指令实现的lambda调用 好.
desugar大概是过字节码生成 (生成表示lambda表达式的字节码), 来支持lambda表达式的. 关于desugar的具体原理, 请戳这里:
https://github.com/bazelbuild/bazel/tree/master/src/tools/android/java/com/google/devtools/build/android/desugar
References
https://developer.android.com/studio/write/java8-support.html
https://jcp.org/en/jsr/detail?id=292
https://www.cnblogs.com/sheeva/p/6344388.html
https://www.cnblogs.com/wade-luffy/p/6058087.html
https://www.kymjs.com/code/2017/10/26/01/
http://blog.csdn.net/zxhoo/article/details/38387141
https://github.com/bazelbuild/bazel/tree/master/src/tools/android/java/com/google/devtools/build/android/desugar
网友评论