Android's Java 8 Support (Android Java 8支持)

本文翻译自:https://jakewharton.com/androids-java-8-support
是技术大神jake wharton的一篇文章,本人能力有限,如果哪里有翻译错误请指出来:
原文:

我已经在家工作了几年,在此期间我听到了程序员们关于Android对不同版本的Java的不断变化的支持的抱怨。每年在Google开发者大会上你都会发现我在篝火聊天询问这些问题或者直接向负责人反馈。在会议上或者其他的开发者活动里,这些讨论和抱怨或多或少也会出现在对话中。这是一个复杂的话题,因为我们对Android的Java支持的讨论是模糊的。对一个版本的Java来说有太多东西:语言特性,字节码,工具,API,JVM等等。
当人们讨论Android的Java 8支持的时候通常指的是语言特性。那么让我们来看看Android的工具链如何处理Java 8的语言特性。

Lambda表达式

Java 8标志性的语言特性是增加了Lambda表达式。这带来了更简洁的代码,而之前我们使用更冗长的结构例如匿名类。

class Java8 {
  interface Logger {
    void log(String s);
  }

  public static void main(String... args) {
    sayHi(s -> System.out.println(s));
  }

  private static void sayHi(Logger logger) {
    logger.log("Hello!");
  }
}

在用javac编译了这个程序之后,用dx工具(dx 是android 把jar转成dex的工具,不确定是这个,如果错误请指出)运行它报了这个错误。

$ javac *.java

$ ls
Java8.java  Java8.class  Java8$Logger.class

$ $ANDROID_HOME/build-tools/28.0.2/dx --dex --output . *.class
Uncaught translation error: com.android.dx.cf.code.SimException:
  ERROR in Java8.main:([Ljava/lang/String;)V:
    invalid opcode ba - invokedynamic requires --min-sdk-version >= 26
    (currently 13)
1 error; aborting

这表示lambda表达式使用了更新的字节码invokedynamic,它在Java 7中被添加进来。正如错误信息所表明,Android对于这种字节码的支持至少是26版本。在写作时很多东西对于应用是不确定的。作为替代,一个叫做“desugaring”的进程被用来把lambda转换为所有android api能兼容的表达。

Desugaring 历史(解释:https://codeday.me/bug/20170721/45291.html)

参考:https://medium.com/@z125617/什麼是d8-dexer-5dd8ff41a3d1
Android工具链的脱糖过程是丰富多彩的。他们的目的是一致的:使更新的语言特性在所有的设备上能运行。
最初一个叫做Retrolambda的第三方工具被应用。他利用内置的机制,这个机制是JVM通常在运行时而不是编译时把lambda转换为类。依据方法的数量,这些生成的类代价非常高。但是这个工具的所做的超时的工作也降低了一些成本。
Android工具组随后发布了一个新的编译器,它可以以更好的性能支持Java 8新特性脱糖。它被内置在Eclipse Java编译器上,但是它支持Dalvik字节码而不是Java字节码。这种Java脱糖非常有效率,但是采用少的话,性能就会更差,并且与其他工具的整合也几乎不支持。
当这个新的编译器被废除时(谢天谢地),一个支持脱糖的Java字节码到Java字节码的转换器被整合进了Android Gradle插件。完成这个工作的是Google的定制系统,Bazel。这个脱糖过程很有效但性能依旧不是很好。它没有最终定型,但是为了一个更好的解决方案的工作仍然在进行中。
D8被发布去取代dx,在dex过程中脱糖,而不是在单独的Java字节码转换过程。d8相比较dx稳定性和效率都提升了一大截。它现在是Gradle插件3.1版本的默认dexer(dex编译器),而且它会在3.2版本变得更加可靠用于脱糖。

D8

使用D8把上面的例子成功编译成Dalvik字节码。

$ java -jar d8.jar \
    --lib $ANDROID_HOME/platforms/android-28/android.jar \
    --release \
    --output . \
    *.class

$ ls
Java8.java  Java8.class  Java8$Logger.class  classes.dex

为了看D8如何对lambda脱糖,我们可以使用dexdump工具,它是Android sdk的一部分。这个工具会输出很多,当然我们只看与我们相关的部分。

$ $ANDROID_HOME/build-tools/28.0.2/dexdump -d classes.dex
[0002d8] Java8.main:([Ljava/lang/String;)V
0000: sget-object v0, LJava8$1;.INSTANCE:LJava8$1;
0002: invoke-static {v0}, LJava8;.sayHi:(LJava8$Logger;)V
0005: return-void

[0002a8] Java8.sayHi:(LJava8$Logger;)V
0000: const-string v0, "Hello"
0002: invoke-interface {v1, v0}, LJava8$Logger;.log:(Ljava/lang/String;)V
0005: return-void
…

如果你之前没有看过任何字节码(Dalvik或其他的),不用担心,大部分是可以看懂的。
在第一段我们的主函数里,字节码0000检索了一个引用,这个引用指向Java8$1类的一个静态实例。因为最初不包含Java8$1类,我们可以推论它是由脱糖生成的。主函数同样不包含任何lambda的痕迹,因此lambda大概率与Java8 1 类 有 关 。 索 引 0002 随 后 用 静 态 实

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值