美文网首页Android...
在Android Studio 3.x 中使用Java8

在Android Studio 3.x 中使用Java8

作者: 元亨利贞o | 来源:发表于2017-12-07 17:58 被阅读1233次

    一. 背景

    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. 为了更好的支持动态语言(GroovyJRubyJythonScala等)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反编译得到如下信息:

    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

        本文链接:https://www.haomeiwen.com/subject/mqkdixtx.html