22、Android性能优化之APK瘦身:资源混淆、代码混淆、移除无用资源、图片格式选择
APK瘦身这件事,说白了就是跟用户的流量和耐心赛跑。我见过太多应用,功能做得不错,但安装包动辄上百兆,用户一看就劝退了。今天咱们聊聊怎么把APK“减减肥”,让它更轻巧、更友好。
核心思路:APK瘦身不是一刀切,而是从代码、资源、图片三个维度分别下手。我习惯按“收益高、风险低”的顺序来排优先级。
22.1 代码混淆:ProGuard与R8
代码混淆,说白了就是把你的类名、方法名、变量名改成a、b、c这种无意义的短名字。这样做有两个好处:一是减小体积,二是增加逆向难度。
我记得刚入行那会儿,有个同事没开混淆,结果APK被人反编译后,整个业务逻辑一览无余。嗯,从那以后我再也不敢省这一步了。
22.1.1 ProGuard vs R8
ProGuard是老牌工具,R8是Google后来推出的替代品。从Android Gradle Plugin 3.4.0开始,R8就成了默认选项。我个人建议直接用R8,它更快,优化效果也更好。
开启方式很简单,在build.gradle里配置:
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
注意,minifyEnabled true就是开启混淆和压缩。R8会自动帮你做三件事:
- 压缩(Shrinking):移除未使用的类和方法
- 混淆(Obfuscation):重命名类名和方法名
- 优化(Optimization):内联方法、简化代码
22.1.2 避坑指南
我曾经踩过一个坑:混淆后某个第三方SDK的反射调用直接崩了。原因是SDK内部用了Class.forName(),但类名已经被混淆改掉了。解决方案是在proguard-rules.pro里加上keep规则。
-keep class com.thirdparty.sdk.** { *; }
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName <fields>;
}
你想想看,如果不加这些规则,Gson解析JSON时也会出问题,因为字段名被改了。所以混淆规则一定要跟第三方SDK的文档对一遍。
22.2 资源混淆:AndResGuard
代码混淆搞定了,资源文件呢?资源文件(图片、布局、字符串)的名字也可以混淆。这就是AndResGuard做的事。
AndResGuard的原理很简单:把res/drawable/icon_home.png改成res/drawable/a.png。路径变短了,APK体积自然就小了。而且资源名被混淆后,别人想猜你的资源结构也难。
配置方式:
apply plugin: 'AndResGuard'
andResGuard {
mappingFile = null
use7zip = true
useSign = true
keepRoot = false
whiteList = [
"R.mipmap.ic_launcher",
"R.drawable.xxx"
]
compressFilePattern = [
"*.png",
"*.jpg",
"*.webp"
]
}
小提示:白名单里的资源不会被混淆。比如启动图标、推送通知图标这些,如果混淆了可能会导致系统找不到资源。我一般会把所有跟系统交互的资源都加进白名单。
22.3 移除无用资源
项目迭代久了,难免会留下一些“僵尸资源”。比如某个图片之前用着,后来UI改版了,但图片还留在文件夹里。这些资源白白占着APK的体积。
Android Studio自带了Lint检查,可以帮你找出未使用的资源。操作路径:Analyze → Run Inspection by Name → Unused resources。
但Lint有个问题:它只能检查静态引用。如果资源是通过getIdentifier()动态加载的,Lint就识别不出来了。我曾经就因为这个删掉了一个动态引用的图片,结果运行时直接报错。
注意:使用Refactor → Remove Unused Resources前,一定要确认资源没有被动态引用。建议先手动检查一遍,或者用resources.arsc文件分析工具辅助。
另外,build.gradle里还可以开启shrinkResources:
android {
buildTypes {
release {
shrinkResources true
minifyEnabled true
}
}
}
shrinkResources会配合代码混淆,自动移除未被引用的资源。注意它必须跟minifyEnabled一起用,否则无效。
22.4 图片格式选择
图片是APK体积的大头。选对图片格式,能省下不少空间。
| 格式 | 特点 | 适用场景 |
|---|---|---|
| PNG | 无损、支持透明 | 图标、UI元素 |
| JPEG | 有损、不支持透明 | 照片、复杂背景图 |
| WebP | 有损/无损、支持透明 | 替代PNG和JPEG |
| SVG | 矢量、无限缩放 | 简单图标、插画 |
我个人强烈推荐WebP。同样的图片,WebP比PNG小30%-50%,比JPEG小20%-30%。而且Android从4.0就开始支持WebP了,兼容性完全没问题。
转换WebP也很简单:在Android Studio里右键图片 → Convert to WebP。或者用在线工具批量转。
我的习惯:UI切图如果是纯色图标,我会让设计师直接给SVG。然后用VectorDrawable加载,体积几乎可以忽略不计。如果是照片类,一律转WebP。只有那些需要透明通道且细节丰富的图,才保留PNG。
22.5 知识体系总览
下面这张图总结了APK瘦身的核心路径,你可以对照着检查自己的项目:
22.6 实战建议
说了这么多,到底怎么落地?我建议按这个顺序来:
- 先开代码混淆:配置
minifyEnabled true,加上keep规则。这一步基本零成本,收益却很大。 - 再开资源压缩:配置
shrinkResources true,配合Lint清理无用资源。 - 然后做资源混淆:接入AndResGuard,注意白名单配置。
- 最后优化图片:把大图转WebP,图标用SVG。
你想想看,这几步做完,APK体积至少能砍掉30%。我有个项目,原本APK是45MB,优化后只剩28MB。用户下载转化率直接提升了5个百分点。
最后说一句:APK瘦身不是一锤子买卖。每次发版前,我都会看一眼APK体积的变化。如果突然变大了,就查查是不是有人不小心塞了张高清大图。养成习惯,你的应用会越来越轻。