首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >APK 体积优化实战:从 50MB 到 15MB 的瘦身之旅

APK 体积优化实战:从 50MB 到 15MB 的瘦身之旅

原创
作者头像
hunter android
修改2026-09-03 17:37:24
修改2026-09-03 17:37:24
20
举报

在移动应用开发中,APK 体积直接影响用户下载意愿和留存率。据 Google 统计,APK 每增加 6MB,安装转化率下降 1%。本文将系统讲解 APK 瘦身的完整方案,从资源优化到代码混淆,帮助你将应用体积压缩 50% 以上。

一、APK 体积分析:找到优化切入点

1.1 使用 APK Analyzer 分析构成

Android Studio 内置的 APK Analyzer 是分析工具的首选:

代码语言:javascript
复制
# 通过命令行分析 APK
./gradlew :app:assembleRelease
# 在 Android Studio 中打开 Build > Analyze APK

典型 APK 构成占比:

  • resources.arsc(资源索引):10-15%
  • classes.dex(代码):20-30%
  • res/(图片、布局等):40-50%
  • lib/(Native 库):15-25%

1.2 使用脚本快速定位大文件

代码语言:javascript
复制
// 自定义 Gradle Task 输出资源占比
tasks.register("analyzeApkSize") {
    doLast {
        val apkFile = file("$buildDir/outputs/apk/release/app-release.apk")
        val entries = mutableMapOf<String, Long>()
        
        java.util.zip.ZipFile(apkFile).use { zip ->
            zip.entries().asSequence().forEach { entry ->
                val category = when {
                    entry.name.startsWith("res/drawable") -> "drawable"
                    entry.name.startsWith("res/layout") -> "layout"
                    entry.name.startsWith("lib/") -> "native-lib"
                    entry.name.endsWith(".dex") -> "dex"
                    else -> "other"
                }
                entries[category] = entries.getOrDefault(category, 0L) + entry.size
            }
        }
        
        entries.toList()
            .sortedByDescending { it.second }
            .forEach { (category, size) ->
                println("$category: ${size / 1024 / 1024}MB")
            }
    }
}

二、资源优化:削减 40% 体积的关键战场

2.1 图片资源压缩与格式选择

WebP 替换 PNG/JPG

WebP 在相同视觉质量下体积减少 25-35%:

代码语言:javascript
复制
// build.gradle.kts
android {
    buildTypes {
        release {
            // 自动转换 PNG 为 WebP
            isCrunchPngs = true
        }
    }
}

手动转换脚本:

代码语言:javascript
复制
# 批量转换 drawable 目录
find res/drawable* -name "*.png" | while read file; do
    cwebp -q 80 "$file" -o "${file%.png}.webp"
    rm "$file"
done

矢量图替换位图

对于图标类资源,VectorDrawable 体积仅为 PNG 的 1/10:

代码语言:javascript
复制
<!-- res/drawable/ic_star.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24">
    <path
        android:fillColor="#FFD700"
        android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
</vector>

2.2 资源混淆:减少资源名长度

使用微信开源的 AndResGuard 工具:

代码语言:javascript
复制
// build.gradle.kts (项目级)
buildscript {
    dependencies {
        classpath("com.tencent.mm:AndResGuard-gradle-plugin:1.2.21")
    }
}

// build.gradle.kts (模块级)
apply(plugin = "AndResGuard")

andResGuard {
    mappingFile = file("resguard-mapping.txt")
    use7zip = true
    useSign = true
    keepRoot = false
    
    whiteList = listOf(
        "R.drawable.icon",  // 保留启动图标
        "R.mipmap.*"
    )
    
    compressFilePattern = listOf(
        "*.png",
        "*.jpg",
        "*.jpeg",
        "*.gif"
    )
}

效果对比:

代码语言:javascript
复制
优化前:res/drawable-xhdpi/ic_home_active.png
优化后:res/r/a.png

2.3 多语言与多密度资源裁剪

仅保留必要语言

代码语言:javascript
复制
android {
    defaultConfig {
        // 仅保留中文和英文
        resourceConfigurations += listOf("zh", "en")
    }
}

动态加载高密度资源

仅打包 xxhdpi,其他密度按需下载:

代码语言:javascript
复制
android {
    defaultConfig {
        // 仅保留一种密度
        resConfigs("xxhdpi")
    }
}

三、代码优化:让 DEX 体积减半

3.1 ProGuard/R8 深度混淆

代码语言:javascript
复制
# proguard-rules.pro
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose

# 移除无用代码
-assumenosideeffects class android.util.Log {
    public static *** v(...);
    public static *** d(...);
    public static *** i(...);
}

# 优化算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

3.2 移除无用依赖

检测未使用的库

代码语言:javascript
复制
./gradlew :app:dependencies > dependencies.txt
# 搜索仅被一个类使用的库,考虑自行实现

替换重型库

重型库

轻量替代

体积减少

Glide

Coil

-800KB

Gson

Moshi

-500KB

OkHttp

HttpURLConnection

-1.2MB

示例:用 Coil 替换 Glide

代码语言:javascript
复制
// 依赖变更
dependencies {
    // implementation("com.github.bumptech.glide:glide:4.15.1")
    implementation("io.coil-kt:coil:2.4.0")  // 减少 800KB
}

// 代码迁移
imageView.load("https://example.com/image.jpg") {
    crossfade(true)
    placeholder(R.drawable.placeholder)
}

3.3 动态特性模块(Dynamic Feature)

将低频功能拆分为按需下载模块:

代码语言:javascript
复制
// feature-vip/build.gradle.kts
plugins {
    id("com.android.dynamic-feature")
}

android {
    namespace = "com.example.feature.vip"
}

dependencies {
    implementation(project(":app"))
}

按需加载逻辑

代码语言:javascript
复制
class FeatureInstaller(private val context: Context) {
    private val splitInstallManager = SplitInstallManagerFactory.create(context)
    
    fun installVipFeature(onSuccess: () -> Unit) {
        val request = SplitInstallRequest.newBuilder()
            .addModule("feature_vip")
            .build()
        
        splitInstallManager.startInstall(request)
            .addOnSuccessListener { sessionId ->
                monitorInstall(sessionId, onSuccess)
            }
            .addOnFailureListener { e ->
                Log.e("Installer", "安装失败", e)
            }
    }
    
    private fun monitorInstall(sessionId: Int, onSuccess: () -> Unit) {
        splitInstallManager.registerListener { state ->
            if (state.sessionId() == sessionId) {
                when (state.status()) {
                    SplitInstallSessionStatus.INSTALLED -> {
                        onSuccess()
                        splitInstallManager.unregisterListener(this)
                    }
                }
            }
        }
    }
}

四、Native 库优化:精准瘦身

4.1 ABI 过滤

现代设备 95% 支持 arm64-v8a:

代码语言:javascript
复制
android {
    defaultConfig {
        ndk {
            // 仅保留主流架构
            abiFilters += listOf("arm64-v8a", "armeabi-v7a")
        }
    }
}

4.2 提取公共 SO 库

当多个架构的 SO 文件相似时,可提取公共部分:

代码语言:javascript
复制
// 自定义 Task 压缩 SO
tasks.register("compressNativeLibs") {
    doLast {
        val libDir = file("$buildDir/intermediates/merged_native_libs/release/out/lib")
        libDir.listFiles()?.forEach { abiDir ->
            exec {
                commandLine("upx", "--best", "--lzma", abiDir.absolutePath + "/*.so")
            }
        }
    }
}

五、实战案例:电商 App 从 50MB 到 15MB

优化前后对比

优化项

优化前

优化后

减少

图片资源

25MB

8MB

-17MB

DEX 代码

12MB

4MB

-8MB

Native 库

10MB

2MB

-8MB

其他资源

3MB

1MB

-2MB

总计

50MB

15MB

-35MB

关键配置清单

代码语言:javascript
复制
// build.gradle.kts
android {
    buildTypes {
        release {
            isMinifyEnabled = true
            isShrinkResources = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    
    defaultConfig {
        resourceConfigurations += listOf("zh", "xxhdpi")
        ndk.abiFilters += listOf("arm64-v8a")
    }
    
    packagingOptions {
        // 移除重复文件
        resources.excludes += listOf(
            "META-INF/*.kotlin_module",
            "META-INF/*.version"
        )
    }
}

六、持续监控:防止体积反弹

6.1 CI/CD 集成体积检查

代码语言:javascript
复制
# .github/workflows/apk-size-check.yml
name: APK Size Check

on: [pull_request]

jobs:
  check-size:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Build APK
        run: ./gradlew assembleRelease
      
      - name: Check APK Size
        run: |
          SIZE=$(stat -c%s app/build/outputs/apk/release/app-release.apk)
          MAX_SIZE=$((20 * 1024 * 1024))  # 20MB 上限
          if [ $SIZE -gt $MAX_SIZE ]; then
            echo "APK 超过 20MB 限制:${SIZE}字节"
            exit 1
          fi

6.2 Gradle 插件自动告警

代码语言:javascript
复制
// buildSrc/src/main/kotlin/ApkSizePlugin.kt
class ApkSizePlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.tasks.register("checkApkSize") {
            doLast {
                val apkFile = project.file("${project.buildDir}/outputs/apk/release/app-release.apk")
                val sizeMB = apkFile.length() / 1024 / 1024
                
                if (sizeMB > 20) {
                    throw GradleException("APK 体积 ${sizeMB}MB 超过 20MB 阈值!")
                }
                
                println("✅ APK 体积检查通过:${sizeMB}MB")
            }
        }
    }
}

七、总结

APK 瘦身是一项系统工程,核心策略包括:

  1. 资源优化:WebP 替换、矢量图、资源混淆、密度裁剪
  2. 代码优化:R8 混淆、移除无用依赖、动态特性模块
  3. Native 优化:ABI 过滤、SO 压缩
  4. 持续监控:CI/CD 集成、自动化告警

通过本文方案,可将 APK 体积压缩 50% 以上。建议从资源优化入手(见效最快),再逐步深入代码和 Native 层。记住,每减少 6MB,安装转化率就能提升 1%。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、APK 体积分析:找到优化切入点
    • 1.1 使用 APK Analyzer 分析构成
    • 1.2 使用脚本快速定位大文件
  • 二、资源优化:削减 40% 体积的关键战场
    • 2.1 图片资源压缩与格式选择
    • 2.2 资源混淆:减少资源名长度
    • 2.3 多语言与多密度资源裁剪
  • 三、代码优化:让 DEX 体积减半
    • 3.1 ProGuard/R8 深度混淆
    • 3.2 移除无用依赖
    • 3.3 动态特性模块(Dynamic Feature)
  • 四、Native 库优化:精准瘦身
    • 4.1 ABI 过滤
    • 4.2 提取公共 SO 库
  • 五、实战案例:电商 App 从 50MB 到 15MB
    • 优化前后对比
    • 关键配置清单
  • 六、持续监控:防止体积反弹
    • 6.1 CI/CD 集成体积检查
    • 6.2 Gradle 插件自动告警
  • 七、总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档