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瘦身的核心路径,你可以对照着检查自己的项目:

APK瘦身核心路径 APK瘦身 代码混淆 资源混淆 图片优化 ProGuard / R8 Keep规则配置 AndResGuard 白名单管理 WebP / SVG shrinkResources 体积减少30%-50%,启动更快,用户更爱

22.6 实战建议

说了这么多,到底怎么落地?我建议按这个顺序来:

  1. 先开代码混淆:配置minifyEnabled true,加上keep规则。这一步基本零成本,收益却很大。
  2. 再开资源压缩:配置shrinkResources true,配合Lint清理无用资源。
  3. 然后做资源混淆:接入AndResGuard,注意白名单配置。
  4. 最后优化图片:把大图转WebP,图标用SVG。

你想想看,这几步做完,APK体积至少能砍掉30%。我有个项目,原本APK是45MB,优化后只剩28MB。用户下载转化率直接提升了5个百分点。

最后说一句:APK瘦身不是一锤子买卖。每次发版前,我都会看一眼APK体积的变化。如果突然变大了,就查查是不是有人不小心塞了张高清大图。养成习惯,你的应用会越来越轻。

公众号:蓝海资料掘金营,微信deep3321