抖音上两个短方法内联减少定义方法数 7 万+,DEX 文件减少一个,包体积收益达到了 1.7M 。常量字段消除上面短方法内联是将方法内容展开到调用处去,我们代码中的一些常量也类似,可以将常量值替换使用处,从而减少字段的声明,这种优化就是常量字段消除的最简单表现 。
我们知道 javac 会做一些 final 类型变量的常量字段消除优化,比如下面的代码:
public class ConstJava {public static final int INTEGER = 1024;public static final String STRING = "this is longstr";public static void constPropagation() {System.out.println("integer:" + INTEGER);System.out.println("string:" + STRING);}}
在编译之后 constPropagation 方法就会变成如下内容,常量直接替换成了字面值,这样相应的 final 字段就变成了无用字段,proguard 就可以将其 shrink 掉 。public static void constPropagation() {System.out.println("integer:1024");System.out.println("string:this is longstr");}
但是比如下面的一些一些 kotlin 代码,编译之后如下, 并未进行传播优化 。当然这里如果添加 const 关键字修改,对应地会进行优化 。class ConstKotlin {companion object {val INTEGER = 1024val STRING = "this is long str"}private val b = 6fun constPropagation(){println("a:$INTEGER")println("s:$STRING")}}
编译后代码:private static final int INTEGER = 1024;@NotNullprivate static final String STRING = "this is long str";public final void constPropagation() {String var1 = "a:" + INTEGER;System.out.println(var1);var1 = "s:" + STRING;System.out.println(var1);}
因此我们可以针对这种 case 进行优化 。另外我们上面说常量字段消除优化之后,对应的字段声明就可以被 proguard 删除,但是项目中有很多 keep 过度的情况,比如下面的规则会导致常量字段声明被保留,这种情况我们可以将字段删除 。
-keep class com.bytedance.android.demo.ConstJava{*;}
优化思路1.收集 static final 类型的变量,并记录其字面值,这里需要排除一些特殊的字段,然后最终确定能删除的字段 。需要排除的字段主要有下面两种:- 用来表示序列化对象版本的 serialVersionUID 字段;
- 有反射使用到的字段,一般来说不太会有反射访问 final 类型变量的情况,但这里还是会尝试分析代码中对字段的反射调用,如果有对应的访问则保留 。
getstatic#48// Field STRING:Ljava/lang/String;
修改为 ldc 指令:ldc#25// String s:this is long str
这里些同学会有疑问,比如一个大的字符串传播到多个类里面不是反而会增大包体积么?的确存在这种可能,不过由于一个 Dex 中所有的类共用一个常量池,所以传播过去如果两个类在同一个 Dex 文件中的话是不会有负向的,反之则会有负向 。
常量字段消除优化总体带来 400KB 左右的包体收益 。R.class 常量内联常量字段消除优化的是常规的 final static 类型,但在我们的代码中,还有另一种类型的常量也可以内联优化 。
在我们 Android 的开发中,常常会用到 R 这个类,它是我们使用资源的最平常的方式 。但实际上,R 文件的生成有着许多不合理的地方,对我们的性能和包大小都造成了极大的影响 。但是要理解这个问题,首先我们需要再理解一次 R 文件是什么 。
推荐阅读
- Android WebRTC 对 AudioRecord 的使用
- TikTok/国际版抖音/海外版抖音2万字干货教程,新手必看
- 作为Android开发,这个知识点一定要知道,官方也改了 2 次
- Android logcat日志封装
- 抖音防烧屏脚本 – Tasker 脚本分享,适用于 OLED 屏幕
- Android开发:使用Kotlin+协程+自定义注解+Retrofit的网络框架
- Android开发:当前项目以Module形式引用别的项目的步骤
- Android恶意木马伪装成游戏APP,通过华为AppGallery分发
- 2021年Android开发新技术动向,未来的路该怎么走?
- Uni-app离线打包Android APK详细教程