在Android Studio 3.x 中使用Java8

一. 背景

android studio 3.0 (一下简称as3)已经发布一段时间了. 现在最新的as版本已经到了3.1了 !
as3有许多新特性, 如: 支持Kotlin, 支持Java8 等.
as3.0之前想用lambda表达式, 你得用Retrolambda这个库来进行兼容.

比较坑的是在我们的安卓项目中有些地方使用lambda会导致编译错误, 而另一些地方则正常! 因此我想干掉retrolambda直接使用as3对java8的支持. 下面说一下如何在as3中使用java8以及其大致原理.

二. 在as3中使用Java8

  1. 配置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

三. 原理

  1. 为了更好的支持动态语言(Groovy、JRuby、Jython、Scala等)Java7增加了新的调用指令invokedynamic (JSR 292). 此指令后来用到了Java8 lambda表达式的实现上 (JSR 335). 然而Dalvik/Art虚拟机并不认识invokedynamic指令, 因此google自己搞了一个叫desugar的东西来实现与invokedynamic指令类似的功能.

  2. 在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反编译得到如下信息:

在Android Studio 3.x 中使用Java8_第1张图片
aaa.png

可以看到框住的部分就是 invokedynmaic指令实现的lambda表达式调用, 这里并没有创建一个 Runnable匿名类.

  1. 再来看看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

你可能感兴趣的:(在Android Studio 3.x 中使用Java8)