R8 如何优化我们的代码(1) -- 减少类的加载

为什么写这个系列

主要是看了 芦大和 P 大的文章,受益颇多,虽然有很多不解的地方,但是大体上已经有了学习的脉络,刚好看到很多人都觉得这几篇文章读起来很吃力,其实我也一样。 要搞懂大佬们的文章,主要是要对 r8 和 art 有一定理解, art 实验起来颇为不易,我们暂且跳过(后面我慢慢补,其实现在我也没太懂,等我学一下再说)

r8对于大部分 app 开发者而言,看起来应该不会有太多阻碍,所以先开坑 r8吧

r8相关部分主要参考jakewharton的 blog

因为 r8 的版本不同,所以部分结果可能和 jw 的原文有出入,大家自行分辨即可

准备工作

准备好独立的 r8 编译环境 具体可以参考官方文档谷歌 r8

不过大部分人肯定会报错, 在构建 r8 之前,我们需要下载一个特殊的工具

bash 复制代码
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git

然后加入到环境变量中

bash 复制代码
export PATH="$PATH:$HOME/depot_tools"

最后按照 google 文档 就可以了

shell 复制代码
$ git clone https://r8.googlesource.com/r8
$ cd r8
$ tools/gradle.py r8

一阵漫长的等待

这个 r8.jar 就是我们的目标产物了,

整个仓库最终大概最终有个 20gb 左右,首次编译我差不多 2 小时才好

不过这里有一个疑问,我在 maven 上直接下载的 r8.jar 是无法独立运行的,不知道和这个构建出来的 r8.jar 具体有啥区别, 有知道的大佬可以指教下

这条路打通以后, 对我们研究 r8 是很有好处的,毕竟可以自己 build 出r8 不用等 agp 升级了。

准备 2 个命令 把 java 和 kotlin 的代码打成 jar 包,方便我们用 jadx 反编译直接看

shell 复制代码
# 指定哪个类是 main 方法的类
jar cvfe main.jar org.example.main *
shell 复制代码
# 指定哪个类是 main 方法的类
kotlinc src/main/kotlin/Main.kt -d main.jar

静态化优化

看下代码:

kotlin 复制代码
package org.example

class Greeter(val greeting:String){
    fun greet(name: String) = "$greeting,$name"
    companion object{
        fun hello() = Greeter("hello")
        fun random() = Greeter("${Math.random()}")
    }
}

fun main() {
    println(Greeter.hello().greet("world"))
    println(Greeter.random().greet("world"))

}

混淆文件

arduino 复制代码
-keepclasseswithmembers class * {
  public static void main(java.lang.String[]);
}
-dontwarn org.jetbrains.annotations.NotNull
-keep class kotlin.** { *; }
-keep class kotlin.Metadata { *; }
-dontwarn kotlin.**
-keepclassmembers class **$WhenMappings {
    <fields>;
}
-keepclassmembers class kotlin.Metadata {
    public <methods>;
}
-dontobfuscate

我们用 kotlinc build 出一个 jar 包

然后放到 jadx 中反编译一下

看下具体的指令:

如果看不懂的话也没关系,我们可以看下代码

其实是符合预期的对吧,

用 r8 处理一下

shell 复制代码
java -cp r8.jar com.android.tools.r8.R8 --release --classfile --output ouput.jar --pg-conf /Users/xxx/Desktop/company_code/R8Test/rules.txt  --lib  /Users/xxx/Library/Android/sdk/platforms/android-33/android.jar /Users/xxx/Desktop/company_code/R8Test/main.jar

然后再用 jadx 反编译一下

还是先看指令

再看代码

经过 r8 处理过的代码,其 伴生类 被抹除,等于少了一个类的加载

如果我们把代码改的简单点,其优化效果会更加明显

kotlin 复制代码
package org.example

class Greeter(val greeting:String){
    fun greet(name: String) = "$greeting,$name"
    companion object{
        fun hello() = Greeter("hello")
        fun random() = Greeter("${Math.random()}")
    }
}

fun main() {
    println(Greeter.hello().greet("world"))
}

这次直接啥都不要了 直接输出对应的字符串

实际上不止是伴生对象,object 单例 也是有类似的优化

修改下我们的代码

kotlin 复制代码
package org.example

class Greeter(val greeting:String){
    fun greet(name: String) = "$greeting,$name"
    companion object{
        fun hello() = Greeter("hello")
        fun random() = Greeter("${Math.random()}")
    }
}

object HelloGreeter{
    fun hello() = Greeter("hello")
}

fun main() {
    println(HelloGreeter.hello())

}

未使用 R8 的时候

还需要取 HelloGreeter 的实例

使用 r8 的时候

优化效果惊人:

这种优化 不仅仅是针对 kotlin,java 也是一样

有兴趣的可以自行做下实验

java 复制代码
package org.example;

public class Thing {
    public static final Thing INSTANCE = new Thing();

    private Thing() {}

    public void doThing() {
        if (Math.random() > 0.5) {
            System.out.println(">0.5");
        }

    }
}

总结一下,R8 的代码优化工作 会帮助我们减少类的加载

相关推荐
诺诺Okami4 小时前
Android Framework-WMS-从setContentView开始
android
前行的小黑炭5 小时前
Android :Compose如何监听生命周期?NavHostController和我们传统的Activity的任务栈有什么不同?
android·kotlin·app
Lei活在当下13 小时前
【业务场景架构实战】5. 使用 Flow 模式传递状态过程中的思考点
android·架构·android jetpack
前行的小黑炭16 小时前
Android 关于状态栏的内容:开启沉浸式页面内容被状态栏遮盖;状态栏暗亮色设置;
android·kotlin·app
用户0919 小时前
Flutter构建速度深度优化指南
android·flutter·ios
PenguinLetsGo20 小时前
关于「幽灵调用」一事第三弹:完结?
android
雨白1 天前
Android 多线程:理解 Handler 与 Looper 机制
android
sweetying1 天前
30了,人生按部就班
android·程序员
用户2018792831671 天前
Binder驱动缓冲区的工作机制答疑
android