最小启动 APK 大小实践

在做 APK 文件的瘦身的实践中,为了了解清楚 APK 文件中到底哪些是必须的,哪些是可以删除的,特地做了本次实践。

使用 Android Studio 创建新项目

平时开发,我们都是从 Android Studio 新建项目开始,不断的编码,丰富 APK 功能;自然 APK 的文件越来越大。
这里我们试试当创建一个新项目时,不加任何自己的代码,编译生成一个 APK 的内容和大小;
环境:AndroidStudio 4.0
1.默认生成 APK 大小:2.2M

参考上图,classes.dex 文件占用空间最大,占比 79.3%;再进一步选中后,发现 androidx 相关库的代码占用 1.5M;
我们知道 androidx 是 google 的扩展框架,不是一个 APK 文件必须的,所以可以去掉相关依赖;

去除androidx:
ConstraintLayout使用LinearLayout代替
Theme.AppCompat.Light.DarkActionBar使用android:Theme.Black.NoTitleBar代替
AppCompatActivity使用Activity代替

2.去除 androidx 库后的APK大小:1.2M


最大文件依然是classes.dex,这次是 kotlin 相关库占用955.1KB。
kotlin 也是 google 后期进入的,可以去除,使用 Java 代替。

去除kotlin:
删除kotlin相关gradle插件,依赖
MainActivity.kt替换为MainActivity.java

3.去除 kotlin 相关库后的 APK 大小:90.4K


去除 androidx 和 kotlin 后,APK 大小仅为 90.4K,比原来的 2.2M, 减少了近 96% 大小;
目前占比最大的是 res/ 下的文件,这里面都是使用到的常量资源: icon(应用图标,APK 可以不配置图标),activity.xml(activity 的布局文件,可以使用 View 对象代替),可以全部去除;

去除res/:
直接删除res目录下所有文件
setContentView(new View(this));

4.去除 res 资源文件目录后的 APK 大小:7.7K

如上图,占比最大的是 MEAT-INF,这是 V1 签名的相关内容,暂时先不优化;
点进 classes.dex 发现 BuildConfig 这个类,这个类是 Android Studio 在编译时自动生成的,程序中可以不适用,所以可以去除;

去除BuildConfig:
// 在build.gradle中配置
android.applicationVariants.all { variant ->
    variant.generateBuildConfig.enabled = false
}

5.去除 BuildConfig 后的 APK 大小:7.6K


如上图,AndroidManifest.xml 占比较大,考虑将该文件内的内容优化;

//将Activity申明简化,后续使用adb命令启动
//注意一定要配置android:exported="true",不然adb不能启动界面
    <application>
        <activity
            android:exported="true"
            android:name=".MainActivity" />
    </application>

6.优化清单文件后,APK 的下载大小:3.4K


MEAT-INF 文件夹下,主要包含 V1 签名信息,可以考虑去除 V1 签名信息文件;仅使用 V2 签名,生成的 APK 文件仅可安装于 Android 7.0 及之后的系统中。

//去除V1签名,配置签名时
    signingConfigs {
        signV2 {
            storeFile file("../sign.jks")
            storePassword "xxx"
            keyAlias "xxx"
            keyPassword "xxx"
            v1SigningEnabled false
            v2SigningEnabled true
        }
    }

7.去除 V1 签名信息后,APK 的下载大小仅 2.7K;

使用 Build Tools 工具编译生成 APK

根据上一小节经验,我们将 app 目录下的资源单独拷贝出来,用作源码,使用安卓的 SDK 构建工具集,就可以编译生成 APK;

这个 APK 也包含四个部分:

  1. resources.arsc
  2. classes.dex
  3. AndroidManifest.xml
  4. META-INF
  1. 编译资源文件,生成 resources.arsc
    aapt2 compile --dir app/src/main/res/ -o package/res.zip
  • --dir 指定资源路径
  • -o 指定输出路径

编译完成后,会在 package 文件夹下看到 res.zip 这个文件;可以尝试解压看看里面内容;

  1. 连接 manifest.xml 文件,生成 R 文件,生成初步 apk 文件
    aapt2 link package/res.zip -I D:/android-sdk-windows/platforms/android-29/android.jar --java package/ --manifest app/src/main/AndroidManifest.xml -o package/res.apk
  • -I:必要参数,指定 android.jar 目录,因为 xml 中可能使用到了例如 android:id 等自带的 android 命名空间
  • o:指定输出 apk 路径
  • —java:指定生成的 R 文件的路径
  • —manifest:必要参数,Manifest 文件中包含了 app 的包名和 application id

初步 apk 文件内容包含 resources.arsc 和 AndroidManifest.xml

  1. 编译 .java 文件为 .class 文件,再转换成 .dex 文件;生成 classes.dex
    javac -encoding utf-8 -target 1.8 -bootclasspath D:/android-sdk-windows/platforms/android-29/android.jar app/src/main/java/com/raise/practice/*.java package/com/raise/practice/R.java -d package/
    d8 package/com/raise/practice/*.class --classpath D:/android-sdk-windows/platforms/android-29/android.jar --output ./

  2. 将 dex 文件添加进 apk
    aapt add package/res.apk classes.dex

  3. 对齐 apk
    zipalign 4 package/res.apk package/app-unsigned-aligned.apk

  4. 签名
    apksigner sign --ks key.jks --out package/app-release.apk package/app-unsigned-aligned.apk

参考:
APK 的前世今生:从 Android 源码到 apk 的编译打包流程