Administrator
发布于 2026-08-26 / 18 阅读
0
0

系列文章《Android 工程师的 100 篇进阶笔记》第 90 篇:Android 应用瘦身:从 100MB 到 20MB 的完整实战

前言

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

-25%

API 14+

WebP Lossless

-30%

API 18+

AVIF

-50%

API 30+

工具链:

# 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)单独分发:

端类型

下载量

全部资源打包

100MB

arm64 + xxxhdpi

30MB

arm64 + xxhdpi

28MB

平均下载量能砍 50%。

2.4 减少 ABI 数(保留 arm64-v8a)

Google Play 在 2019 年起就只要求 64-bit:

android {
    defaultConfig {
        ndk {
            abiFilters("arm64-v8a")  // 只保留主流
        }
    }
}

仅去掉 armeabi-v7a 即可省 30% 的 native 体积。

2.5 dex + 库剪裁

库

剪裁前

剪裁后

com.android.support.*

1.2MB

0(迁移到 AndroidX)

多余 SDK

1.5MB

0

JUnit

0.6MB

仅 test

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

类型

时机

Install-time

安装时

Fast Follow

安装完后立即后台拉

On-Demand

业务需要时拉

游戏、地图、教育类应用必备。

四、运行时压缩 & 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")
}

每次发版前都得看包大小是否超预算。

六、瘦身效果预期

阶段

优化手段

中等规模 App

基线

100MB

100MB

R8 + 资源压缩

isMinifyEnabled, isShrinkResources

70MB

App Bundle 单 ABI + dpi

abiFilters("arm64-v8a")

35MB

WebP 替代 PNG

cwebp 批量

28MB

Feature Module 按需

SplitInstall

25MB

Asset Delivery

大资源按需下载

18-22MB

功能对齐(去掉无用)

手动整理

15-20MB

实际工程上,50MB 以上的瘦到 20MB 是 6 周时间工作。

七、瘦身的"反平衡"思考

小心:瘦身不能过头。下表是红线:

现象

后果

只支持 arm64

32-bit 设备无法升级

不支持 mdpi

老旧设备显式模糊

砍了 unzip 库

资源包无法解压

砍了摄像头模块

部分业务崩溃

做小优化不是无脑删除。每个删除都要追溯业务影响。

八、瘦身工具

工具

用途

Android Studio Build Analyzer

构建产物可视化

bundletool

AAB / APK 分析

aapt2

Manifest & 资源分析

apk-shrinker (第三方)

批量处理

Unreal Optimization Toolkit

高级优化

Resource Shrinker (AS)

内置

R8 / ProGuard 报告

minify 输出

# bundletool 分析
bundletool get-size total --bundle=app.aab
# 输出:
# INSTALL: 18.3MB
# DEVICE: 22.1MB
# ASSET: 48.0MB

九、瘦身后的验证步骤

  1. 功能验证:所有原有功能都跑通

  2. 灰度发布:1% → 5% → 20% → 100%

  3. Crash & ANR 监控:阈值不能恶化

  4. 启动时间:不能比原来慢

  5. 转化率:观察升级转化率是否提升

十、瘦身的工程 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 工具文档


评论