Android D8 编译器 和 R8 工具

compile_with_r8.png

注意,如果我们在 build.gradle 中配置了 useProguard = false 则不管是否开启 R8 编译都会使用 R8 进行压缩代码。

2. 脱糖

Android Studio 为使用部分 Java 8 语言功能及利用这些功能的第三方库提供内置支持。默认工具链对 javac 编译器的输出执行字节码转换(称为 desugar),从而实现新语言功能。

脱糖即在编译阶段将在语法层面一些底层字节码不支持的特性转换为基础的字节码结构,(比如 List 上的泛型脱糖后在字节码层面实际为 Object); Android 工具链对 Java8 语法特性脱糖的过程可谓丰富多彩,当然他们的最终目的是一致的:使新的语法可以在所有的设备上运行。

经过上面 D8R8 的了解,D8 已经支持脱糖,让 Java 8 提供的特性(如 lambdas)可以转换成 Java 7 特性。把脱糖步骤集成进 D8 影响了所有读或写 .class 字节码的开发工具,因为它会使用 Java 8 格式。可以在 gradle.properties 中禁用脱糖。

android.enableIncrementalDesugaring=false.
android.enableDesugar=false

2.1 Lambda 表达式

Java 8 中一个重大变更是引入 Lambda 表达式。

public class Lambda {
public static void main(String[] args) {
logDebug(msg-> System.out.println(msg), “HelloWorld”);
}

static void logDebug(Logger logger, String msg) {
logger.log(msg);
}

@FunctionalInterface
interface Logger {
void log(String msg);
}
}

通过 javac 指令将上面的 Lambda.java 转换为字节码。

$javac Lambda.java

接下来通过 javap -v 指令查看字节码的详细内容:

dengshiweideMacBook-Pro:Desktop dengshiwei$ javap -v Lambda.class
Classfile /Users/dengshiwei/Desktop/Lambda.class
Last modified 2019-5-12; size 1120 bytes
MD5 checksum 67301305720e60d4ef1ff286769768e6
Compiled from “Lambda.java”
public class Lambda
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #9.#25 // java/lang/Object.“”😦)V
#2 = InvokeDynamic #0:#30 // #0:log:()LLambdaKaTeX parse error: Expected 'EOF', got '#' at position 9: Logger; #̲3 = String …Logger;Ljava/lang/String;)V
#5 = InterfaceMethodref #10.#33 // LambdaKaTeX parse error: Expected 'EOF', got '#' at position 34: …lang/String;)V #̲6 = Fieldref …Logger
#11 = Utf8 Logger
#12 = Utf8 InnerClasses
#13 = Utf8
#14 = Utf8 ()V
#15 = Utf8 Code
#16 = Utf8 LineNumberTable
#17 = Utf8 main
#18 = Utf8 ([Ljava/lang/String;)V
#19 = Utf8 logDebug
#20 = Utf8 (LLambdaKaTeX parse error: Expected 'EOF', got '#' at position 29: …lang/String;)V #̲21 = Utf8 …mainKaTeX parse error: Expected 'EOF', got '#' at position 3: 0 #̲22 = Utf8 …Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#28 = MethodType #22 // (Ljava/lang/String;)V
#29 = MethodHandle #6:#42 // invokestatic Lambda.lambda$mainKaTeX parse error: Expected 'EOF', got '#' at position 25: …lang/String;)V #̲30 = NameAndTyp…Logger;
#31 = Utf8 HelloWorld
#32 = NameAndType #19:#20 // logDebug:(LLambdaKaTeX parse error: Expected 'EOF', got '#' at position 29: …lang/String;)V #̲33 = NameAndTyp…Logger
#41 = Methodref #50.#51 // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandlesKaTeX parse error: Expected 'EOF', got '#' at position 176: …voke/CallSite; #̲42 = Methodref …mainKaTeX parse error: Expected 'EOF', got '#' at position 25: …lang/String;)V #̲43 = Utf8 …Logger;
#45 = Utf8 java/lang/System
#46 = Utf8 out
#47 = Utf8 Ljava/io/PrintStream;
#48 = Utf8 java/io/PrintStream
#49 = Utf8 println
#50 = Class #53 // java/lang/invoke/LambdaMetafactory
#51 = NameAndType #54:#57 // metafactory:(Ljava/lang/invoke/MethodHandlesKaTeX parse error: Expected 'EOF', got '#' at position 173: …voke/CallSite; #̲52 = NameAndTyp…mainKaTeX parse error: Expected 'EOF', got '#' at position 25: …lang/String;)V #̲53 = Utf8 …Lookup
#56 = Utf8 Lookup
#57 = Utf8 (Ljava/lang/invoke/MethodHandlesKaTeX parse error: Expected 'EOF', got '#' at position 173: …voke/CallSite; #̲58 = Class …Lookup
#60 = Utf8 java/lang/invoke/MethodHandles
{
public Lambda();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object.“”😦)V
4: return
LineNumberTable:
line 1: 0

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: invokedynamic #2, 0 // InvokeDynamic #0:log:()LLambdaKaTeX parse error: Expected 'EOF', got '#' at position 26: … ldc #̲3 …Logger;Ljava/lang/String;)V
10: return
LineNumberTable:
line 3: 0
line 4: 10

static void logDebug(Lambda L o g g e r , j a v a . l a n g . S t r i n g ) ; d e s c r i p t o r : ( L L a m b d a Logger, java.lang.String); descriptor: (LLambda Logger,java.lang.String);descriptor:(LLambdaLogger;Ljava/lang/String;)V
flags: ACC_STATIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: invokeinterface #5, 2 // InterfaceMethod LambdaKaTeX parse error: Expected 'EOF', got '}' at position 81: …7: 0 line 8: 7 }̲ SourceFile: "L…Logger of class Lambda
public static final #56= #55 of #58; //Lookup=class java/lang/invoke/MethodHandlesKaTeX parse error: Expected 'EOF', got '#' at position 69: …rapMethods: 0: #̲27 invokestatic…Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#28 (Ljava/lang/String;)V
#29 invokestatic Lambda.lambda$main$0:(Ljava/lang/String;)V
#28 (Ljava/lang/String;)V

在上面的详细字节码信息中,有两个重要信息需要我们注意:

major version: 52

主版本号是 52 对应的是 JDK 1.8

invokedynamic 指令

invokedynamicJava7 开始提出来,但是实际上 javac 并不支持生成 invokedynamic。在 Java 8 中,javac 能够生成 invokedynamic 指令, 比如 lambda 表达式。

2.2 dx 指令打包

首先我们采用 buildToolsVersion = 22.0.1 版本来编译上面的 class 文件。

$/Users/dengshiwei/Library/Android/sdk/build-tools/22.0.1/dx --dex *.class

出错了!

UNEXPECTED TOP-LEVEL EXCEPTION:
com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000)
at com.android.dx.cf.direct.DirectClassFile.parse0(DirectClassFile.java:472)
at com.android.dx.cf.direct.DirectClassFile.parse(DirectClassFile.java:406)
at com.android.dx.cf.direct.DirectClassFile.parseToInterfacesIfNecessary(DirectClassFile.java:388)
at com.android.dx.cf.direct.DirectClassFile.getMagic(DirectClassFile.java:251)
at com.android.dx.command.dexer.Main.processClass(Main.java:704)
at com.android.dx.command.dexer.Main.processFileBytes(Main.java:673)
at com.android.dx.command.dexer.Main.access$300(Main.java:83)
at com.android.dx.command.dexer.Main 1. p r o c e s s F i l e B y t e s ( M a i n . j a v a : 602 ) a t c o m . a n d r o i d . d x . c f . d i r e c t . C l a s s P a t h O p e n e r . p r o c e s s O n e ( C l a s s P a t h O p e n e r . j a v a : 170 ) a t c o m . a n d r o i d . d x . c f . d i r e c t . C l a s s P a t h O p e n e r . p r o c e s s ( C l a s s P a t h O p e n e r . j a v a : 144 ) a t c o m . a n d r o i d . d x . c o m m a n d . d e x e r . M a i n . p r o c e s s O n e ( M a i n . j a v a : 632 ) a t c o m . a n d r o i d . d x . c o m m a n d . d e x e r . M a i n . p r o c e s s A l l F i l e s ( M a i n . j a v a : 510 ) a t c o m . a n d r o i d . d x . c o m m a n d . d e x e r . M a i n . r u n M o n o D e x ( M a i n . j a v a : 280 ) a t c o m . a n d r o i d . d x . c o m m a n d . d e x e r . M a i n . r u n ( M a i n . j a v a : 246 ) a t c o m . a n d r o i d . d x . c o m m a n d . d e x e r . M a i n . m a i n ( M a i n . j a v a : 215 ) a t c o m . a n d r o i d . d x . c o m m a n d . M a i n . m a i n ( M a i n . j a v a : 106 ) . . . w h i l e p a r s i n g L a m b d a 1.processFileBytes(Main.java:602) at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:170) at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144) at com.android.dx.command.dexer.Main.processOne(Main.java:632) at com.android.dx.command.dexer.Main.processAllFiles(Main.java:510) at com.android.dx.command.dexer.Main.runMonoDex(Main.java:280) at com.android.dx.command.dexer.Main.run(Main.java:246) at com.android.dx.command.dexer.Main.main(Main.java:215) at com.android.dx.command.Main.main(Main.java:106) ...while parsing Lambda 1.processFileBytes(Main.java:602)atcom.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:170)atcom.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144)atcom.android.dx.command.dexer.Main.processOne(Main.java:632)atcom.android.dx.command.dexer.Main.processAllFiles(Main.java:510)atcom.android.dx.command.dexer.Main.runMonoDex(Main.java:280)atcom.android.dx.command.dexer.Main.run(Main.java:246)atcom.android.dx.command.dexer.Main.main(Main.java:215)atcom.android.dx.command.Main.main(Main.java:106)...whileparsingLambdaLogger.class

bad class file magic 魔数无法识别,原因是由于 JDKdx 的版本不匹配导致的。同时 version (0034.0000) 版本转换为十进制就是 52 即对应 JDK 1.8。这里我们更改版本为:28.0.1

dengshiweideMacBook-Pro:dex dengshiwei$ /Users/dengshiwei/Library/Android/sdk/build-tools/28.0.1/dx --dex *.class
Uncaught translation error: com.android.dx.cf.code.SimException: ERROR in Lambda.main:([Ljava/lang/String;)V: invalid opcode ba - invokedynamic requires --min-sdk-version >= 26 (currently 13)
1 error; aborting

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

[外链图片转存中…(img-69dI6kBE-1718818092885)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取
集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。**

[外链图片转存中…(img-69dI6kBE-1718818092885)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值