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 的代码优化工作 会帮助我们减少类的加载

相关推荐
码云骑士12 分钟前
76-全量微调vs-LoRA-vs-QLoRA-三种微调方式对比与选型
android
光头闪亮亮21 分钟前
Fyne ( go跨平台GUI )项目实战-WebView 组件开发技术详解
android·go
嵌入式小周1 小时前
Genymotion 安卓模拟器在 Intel 芯片 Mac 上的运行(附带下载方式)
android·macos
zzq77973 小时前
Android 16 API 36 升级后 APP 加固兼容性问题解析
android·开发语言·安全·kotlin·安卓·安全架构
2601_961391464 小时前
KMP全栈开发:从Android到AI Agent的技术演进与实践
android·人工智能
2501_9159184112 小时前
深入对比iOS开发中常用性能监控工具的底层原理与优缺点分析
android·ios·小程序·https·uni-app·iphone·webview
my_power52014 小时前
android中Activity生命周期函数的职责
android
Sirens.14 小时前
从参考 iCost 到做自己的 OneLedger:一个 Android 本地记账 App 的开发记录
android·kotlin·room·jetpack compose·记账 app
qq_4480111614 小时前
C语言中的变量和函数的定义与声明
android·c语言·开发语言
码农coding18 小时前
android 12 中的VSYNC的请求
android