前言
App 包大小直接影响下载转化率:每多 10MB,转化率掉 5%-10%。本篇是 100 篇系列的收官之作,从工程实战角度,拆解 APK / AAB 瘦身的完整路线图。
一、从哪里下手:先测量、再优化
# 生成 APK
./gradlew assembleRelease
# 用 aapt2 分析
aapt2 dump badging app-release.apk
# APK size: 38 MB
# zipinfo 看具体内容
zipinfo -l app-release.apk | sort -n -k1
# 看哪个文件占比最大
# AAB 用 bundle-tool
bundletool build-apks --bundle=app.aab --output=app.apks
unzip app.apks -d extracted/
ls -la extracted/splits/
启动时的 Baseline Profiles(前面启动框架那一篇讲过)会让单个用户的下载量大幅下降——这是 App Bundle 的核心能力之一:把超大资源分包、按需下发。
二、瘦身九式
2.1 全量法:去掉无用代码
android {
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles("proguard-rules.pro")
}
}
}
R8 默认会做混淆、死代码消除、资源去重。如果配合 isShrinkResources = true,unused resource 也会被剔除。
经过一段时间调教,能砍掉 30%。
2.2 WebP / AVIF 替代 PNG
工具链:
# PNG → WebP(无损)
cwebp -lossless original.png -o original.webp
# PNG → WebP(有损,quality 75)
cwebp -q 75 original.png -o original.webp
# PNG → AVIF
avifenc original.png original.avif
效果:一个 100MB 项目,含图片 40MB;改 WebP 后能省 10MB。
2.3 资源分包
android {
bundle {
density {
enableSplit = true
}
language {
enableSplit = true
}
abi {
enableSplit = true
}
}
}
让 App Bundle 把 4 个 dpi(hdpi, xhdpi, xxhdpi, xxxhdpi)、3 个 ABI(arm64-v8a, armeabi-v7a, x86_64)单独分发:
平均下载量能砍 50%。
2.4 减少 ABI 数(保留 arm64-v8a)
Google Play 在 2019 年起就只要求 64-bit:
android {
defaultConfig {
ndk {
abiFilters("arm64-v8a") // 只保留主流
}
}
}
仅去掉 armeabi-v7a 即可省 30% 的 native 体积。
2.5 dex + 库剪裁
2.6 减少资源文件
很多项目因历史原因留有:
/res/drawable-xhdpi/ // 与 hdpi 重复
/res/drawable-unused/ // 工程残留
写脚本扫描每个 drawable 的引用:
# 找 unused drawable
for f in $(find app/src/main/res -name "*.png"); do
name=$(basename $f .png)
if ! grep -r "R.drawable.$name" app/src/main/ 2>/dev/null | grep -q "Kotlin\|Java"; then
echo "Unused: $name"
fi
done
2.7 字体优化
<!-- res/font/app_font.xml -->
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
<font android:fontStyle="normal" android:fontWeight="400"
android:font="@font/inter_regular" />
</font-family>
如果用了远程字体,避免内置字体文件;想用建议用 ttf2woff + subset(仅包含需要的字符)。
2.8 资源 ID 常量化
有些工程维护 200KB 的 ids.xml("home_icon"、"feed_id" …),这种属于历史遗留。
# 删冗余 ID
sed '/<!--[[:space:]]*home_icon -->/d' res/values/ids.xml
2.9 编译选项优化
android {
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
resValue "string", "app_name", "MyApp"
}
}
}
R8 + 资源压缩 + resValue 拆分是默认配置。
三、拆分粒度优化
3.1 Architecture Split
App Bundle 默认按 abi、density、language 拆分。如果业务允许,可以再加 feature modules:
include ':app', ':feature_home', ':feature_mall', ':feature_user'
// app 的 build.gradle.kts
dependencies {
implementation(project(":feature_home"))
implementation(project(":feature_mall"))
implementation(project(":feature_user"))
}
按需下发:
SplitInstallManagerFactory.create(context)
.requestInstall(
SplitInstallRequest.newBuilder()
.addModule("feature_mall")
.build()
)
.addOnSuccessListener { /* done */ }
按模块分发能把首次安装从 60MB 降到 25MB。
3.2 Asset Delivery
Android 11+ 提供:
// 大资源走 Play Asset Delivery(fast-follow / on-demand)
// 100MB 的教学视频不打进 APK
游戏、地图、教育类应用必备。
四、运行时压缩 & Lazy 加载
// 业务按需加载
object AssetIndex {
fun loadLargeIcon(): ImageBitmap = runBlocking {
withContext(Dispatchers.IO) {
BitmapFactory.decodeStream(
assets.open("icons/hero.webp").inputStream()
).asImageBitmap()
}
}
}
不要把所有图片一口气加载。
五、监控与持续治理
// build.gradle.kts
tasks.register("reportSize") {
doLast {
val apk = file("build/outputs/apk/release/app-release.apk")
val size = apk.length() / 1024 / 1024
println("APK size: ${size}MB")
// 如果超过预算,调用 gradle fail
if (size > 35) throw GradleException("APK size exceeded 35MB budget")
}
}
tasks.named("assembleRelease") {
finalizedBy("reportSize")
}
每次发版前都得看包大小是否超预算。
六、瘦身效果预期
实际工程上,50MB 以上的瘦到 20MB 是 6 周时间工作。
七、瘦身的"反平衡"思考
小心:瘦身不能过头。下表是红线:
做小优化不是无脑删除。每个删除都要追溯业务影响。
八、瘦身工具
# bundletool 分析
bundletool get-size total --bundle=app.aab
# 输出:
# INSTALL: 18.3MB
# DEVICE: 22.1MB
# ASSET: 48.0MB
九、瘦身后的验证步骤
功能验证:所有原有功能都跑通
灰度发布:1% → 5% → 20% → 100%
Crash & ANR 监控:阈值不能恶化
启动时间:不能比原来慢
转化率:观察升级转化率是否提升
十、瘦身的工程 SOP
1. Size 测量
├─ APK 文件大小
├─ AAB 总尺寸
└─ 解压后各模块占比
2. 资源优化
├─ PNG → WebP
├─ 多余 drawable 删除
├─ 字体子集
└─ 无用 IDs 清理
3. 代码优化
├─ R8 / minify
├─ shrinkResources
└─ 死代码消除
4. 分包优化
├─ ABI splits
├─ Density splits
├─ Language splits
├─ Feature modules
└─ Asset delivery
5. CI 验收
├─ 阈值检查
└─ 报告推送
把这个 SOP 放进每次发版流程里。
小结
App 瘦身不是一次性项目,是持续的工程治理。每次发版前要看、每次业务功能上线后要看、每季度做一次整体回顾。20MB 以内是商业 App 当前最低门槛,达到即获得 5%-10% 的转化率优势。
本篇是系列第 90 篇。到 90 篇为止,《Android 工程师的 100 篇进阶笔记》已经覆盖从初级到专家的大部分主题。剩下 10 篇将进入"垂直专题"——大模型工程化、Android 18 新特性、车机开发、隐私工程等多个方向。
参考阅读
Android Developers:Reduce APK size
Android Developers:Analyze with Build Analyzer
App Bundle / Play Asset Delivery 官方文档
cwebp / avifenc 工具文档