包体积优化方案
优势
减少包体积,让用户在更快的速度下载使用应用,提高用户下载转化率。
apk组成
Android 项目最终会编译成一个 .apk 后缀的文件,实际上它就是一个 压缩包。它内部还有很多不同类型的文件,大致分为以下四类:
- 代码相关:classes.dex,我们在项目中所编写的 java 文件,经过编译之后会生成一个 .class 文件,而这些所有的 .class 文件呢,它最终会经过 dx 工具编译生成一个 classes.dex。
- 资源相关:res、assets、编译后的二进制资源文件 resources.arsc 和 清单文件 等等。res 和 assets 的不同在于 res 目录下的文件会在 .R 文件中生成对应的资源 ID,而 assets 不会自动生成对应的 ID,而是通过 AssetManager 类的接口来获取。此外,每当在 res 文件夹下放一个文件时,aapt 就会自动生成对应的 id 并保存在 .R 文件中,但 .R 文件仅仅只是保证编译程序不会报错,实际上在应用运行时,系统会根据 ID 寻找对应的资源路径,而 resources.arsc 文件就是用来记录这些 ID 和 资源文件位置对应关系 的文件。
- so相关:lib目录下的文件。
此外,还有 META-INF,它存放了应用的 签名信息,其中主要有 3个文件,如下所示:
- MANIFEST.MF:其中每一个资源文件都有一个对应的 SHA-256-Digest(SHA1) 签名,MANIFEST.MF 文件的 SHA256(SHA1) 经过 base64 编码的结果即为 CERT.SF 中的 SHA256(SHA1)-Digest-Manifest 值。
- CERT.SF:除了开头处定义的 SHA256(SHA1)-Digest-Manifest 值,后面几项的值是对 MANIFEST.MF 文件中的每项再次 SHA256(SHA1) 经过 base64 编码后的值。
- CERT.RSA:其中包含了公钥、加密算法等信息。首先,对前一步生成的 CERT.SF 使用了 SHA256(SHA1)生成了数字摘要并使用了 RSA 加密,接着,利用了开发者私钥进行签名。然后,在安装时使用公钥解密。最后,将其与未加密的摘要信息(MANIFEST.MF文件)进行对比,如果相符,则表明内容没有被修改。
Apk分析
这里我们使用的是AndroidStudio2.2之后提供的Analyze APK。
Analyze APK具有如下优势:
- 可以直观地查看到 APK 的组成,比如大小、占比等等。
- 查看 dex 文件的组成。
- 对不同的 APK 进行对比分析。
我们将需要优化的包拖到AS中,AS会自动使用Analy APK打开,这样就可以APK文件的绝对路径及各组成文件的百分比。
根据APK结构可以分享一个应用的资源主要区分为三种,项目代码、资源文件、支持对应CPU架构的so文件,因此我们的包体积优化也是围绕这三种资源做优化。
一、代码优化
1.ProGuard混淆
混淆的形式:
- 改变代码中的元素,比如:类、函数、变量的名字变成无意义的字符。增加反编译阅读难度。
- 重写代码中的部分逻辑,将其变成功能等价但难以理解的形式。比如:循环的指令、结构体。
- 打乱代码的格式。比如:增加或者删除一些空格。
混淆的作用:
- 瘦身:检测到无用的资源、类、方法,将其移除,并且能对代码进行深度优化。
- 安全:将代码中的元素变成简短,无意义的字符,增加反编译阅读难度,使代码更为安全。
混淆的流程:
混淆的流程分为四个步骤:
压缩:减少应用体积,移除未被使用的类和成员,在优化后会再次执行。
优化:在字节码级别执行优化,让应用运行更快。
混淆:增大反编译难度,将类和成员随机命名。
预校验:在 Java 平台上对处理后的代码进行预检,确保加载的 class文件是可执行的。
开启代码混淆:
buildTypes {
debug{
debuggable true
zipAlignEnabled true
minifyEnabled false
signingConfig signingConfigs.release
resValue("bool", "config_center_is_debug_mode", "true")
}
release {
zipAlignEnabled true//开启zipAlign可以让安装包中的资源按4字节对齐,这样可以减少应用在运行时的内存消耗
minifyEnabled true //开启混淆
shrinkResources true //打包移除未使用资源
multiDexKeepProguard file('multidex-config.pro')
//混淆文件的位置,它默认打开了优化开关。并且,我们可在配置混淆文件将android.util.Log置为无效代码,
// 以去除apk中打印日志的代码。
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'//混淆规则
signingConfig signingConfigs.release
resValue("bool", "config_center_is_debug_mode", "false")
}
}
使用注意:
debug环境不要混淆,增加打包编译速度
实战
将代码进行混淆后,包分析如下
缩减了34%
打包后会在release目录增加混淆后的包和mapping.txt文件,mapping.txt是混淆规则,当在线上发生崩溃日志使,可能通过mapping.txt文件反推回原始代码。
2.三方库优化
- 相同的三方库保留一个
- 优先选择体积小的三方库
- 当三方库有多个功能库时,尽量最小化依赖需要的依赖库
二、so文件优化
- armeabi-v7a:第7代及以上的 ARM 处理器,2011年大部分设备使用
- arm64-v8a:第8代、64位 ARM 处理器,极少数设备,如三星 Galaxy S6
- armeabi:第5、6代 ARM 处理器,早期手机
- x86:平板、模拟器
- x86-64:64位的平板
目前Android主流设备使用的CPU架构是32位的armeabi-v7a和64位的arm64-v8a,这代表了目前ARM处理器的主流水平。x86架构在Android上的使用较少,主要应用在一些平板电脑上
实战
保留主流的架构:
包大小缩减了12%
三、资源优化
1.压缩图片资源
使用免费的压缩文件将图片做压缩替换
2.图片资源格式转换
要做好图片体积优化,我们需要了解各种图片格式的优缺点,针对不同业务使用不同的图片格式。
格式
|
描述
|
优点
|
缺点
|
适用场景
|
jpg
|
进行了有损压缩,去掉了图片质量为前提,尽可能压缩大小
|
适合存储照片
|
没有alpha,保存图片会自动保存为白色
|
对透明通道没有要求的高保真大图要存储到磁盘的图片,优先使用 jpg
|
png
|
无损压缩,图片会比jpg大
|
支持alpha通道,显示效果比较好
|
-
|
一些普通图标切图的话就使用 png
|
webp
|
支持有损和无损压缩,相同质量图片webp体积更新
|
1、无损压缩情况下,相同质量的 webp 图片,文件大小比 png 小 26% 2、有损压缩情况下,具有相同精度的 webp 图片,文件大小比 jpg 小 25%-34%
3、webp 格式支持 alpha 通道,一个无损压缩的 webp 图片要支持透明度只需要 22% 的额外文件大小
|
不能完全兼容所有 平台
|
有用到网络传输且图片比较大的场景,比如和服务器通信获取网络图片,优先使用 webp,其次是 png
|
svg
|
svg 是无损的矢量图,是矢量图中的一种,它的特点是 使用 xml 描述图片
|
不会失真,体积小
|
渲染速度慢
不支持投影、模糊、使用场景有限
|
图片体积小要求不高的纯色小图标可以使用 svg
|
这里将项目中特别大的png转换为了webp
3.保留少量的图片目录
保留现xxhdpi和xxxhdpi
3.清除无用资源
使用androidstudio自带的Lint检查工具检测
实际测试发现,lint检测存在一些问题,可能会移除一些正在使用的资源文件,如果存在动态获取资源id的方式,那么这个资源也会认为没有使用,并且在我们上面进行混淆的时候,实际已经做过无用资源的移除了。
4.去除多语言
在app的build.gradle中的defaultConfig中设置
resConfigs ‘zh-rCN’
当前项目只有中文语言,这里去除的是引用的第三方库的多语言。
5.SVGA动画文件优化
当前项目中送礼物SVGA动画资源耗费了约20M,考虑和后端沟通通过后台下载方式优化。
实战
缩减了23%,由此可见使用webp能大大缩小包的体积。
四、其他优化
除以上外,我们还可以做的优化有AndResGuard 资源混淆。
AndResGuard 是一个资源压缩的工具,和 ProGuard 混淆代码类似,但只作用于资源文件,它会将资源路径变短例如 res/drawable/wechat 变成 r/d/a,以及重命名资源文件例如 wechat.png 变成 a.png,使用的 7zip 压缩方式能一定程度缩减包体积。
但是考虑以下场景,看项目是否使用。
- 动态换肤:动态换肤是通过代码查找资源路径动态替换的,因为资源路径会被 AndResGuard 混淆会导致换肤失败
- 项目中有大量用到 ConstraintLayout 的 Group:Group 使用 constraint_referenced_ids 属性将多个控件作为一个整体引用,使用了 AndResGuard 要引用控件 id 生效,引用的控件 id 都要加到白名单,否则操作 Group 控制控件会失效
总结
包体积优化第一版本优化完毕,由原始的181.2M缩减到了75M,包大小减少了58%,其中主要是从代码、so库、资源三个方面入手进行优化,在实际业务场景中,需要进行apk分析适合自己的业务场景。
参考链接: