背景
随着业务迭代,apk体积逐渐变大。项目中积累的无用资源,未压缩的图片资源等,都为apk带来了不必要的体积增加。调研了一些apk瘦身的方法后,总结如下。
可用方法
1.删除无用资源
2.删除无用方法
3.混淆(方法混淆&资源混淆
4.使用一套图片资源
5.图片压缩/其他图片格式
6.so兼容一个平台,动态下载
7.精简第三方库
删除无用资源
1⃣️开启ShrinkResource
在build.gradle中配置shrinkResource=true
首先介绍一下shrinkResource的原理:在打包过程中会多出一个task,通过调用一个analyze方法进行无用资源的分析并进行处理,具体过程
1)根据R文件生成资源表,然后遍历所有的class文件,分析class中使用的资源,标记可达。
2)分析Manifest res,分析资源文件引用的其他资源,标记可达。
3)调用keepPossiblyReferencedResources,标记可能到达的资源。比如通过getIdentifier动态获取的资源⬇以下代码会将所有带有img_前缀的资源标记为已使用。
String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());
(如果想开启严格检测,不标记可能到达的资源,在keep.xml配置 tools:shrinkMode="strict",对于不进行严格检测的资源,配置tools:keep 风险:开启严格检测可能导致程序有bao
⚠️然而,经过实验发现,开启shrinkResource并没有使apk变小。因为在打包时,无用资源并没有直接被清理,而是把部分无用资源用更小的东西代替掉。
2⃣️开启Lint检测
在Android Studio中 Analyze-Run Inspection by Name-unused resource(过程与ShrinkResource类似)
注意下图不要打勾,无效的id对apk体积增加微乎其微,删去却可能引起bug,得不偿失。
还有一种要注意的情况
例如 <String name="xxx"><font size="16">请输入用户名</font></String>
其中font size="16" 会被判定为无效资源,但是lint直接清除时,会把font标签内的内容全部删除,清理后的效果
<String name="xxx"></String>(文字丢失了,lint暂时还不够智能,但这种情况比较少见,review的时候仔细检查一下)
⚠️lint的检测可能存在遗漏,如第一次找到100个无用资源,删除后第二次查找后,可能还存在几个无用资源,可以多查几次
3⃣️删除无用的语言资源
在语言的配置上,只保留需要用到的语言,目前项目只保留中文即可,有些项目可能保留英文
defaultConfig{
resConfigs "zh"
}
删除无用方法
1⃣️开启minifyEnabled
实验发现,开启后方法数从13w缩减到10w,效果还是比较显著的
2⃣️使用Lint查找无用符号并删除
Analyze——Run Inspection by Name——unused symbol(Kotlin)
Analyze——Run Inspection by Name——unused declaration (Java)
批量删除的时候会一直提问是否safe delete
开启了minifyEnabled后无用声明等基本不会增加apk的大小
3⃣️内部类访问宿主类中的private成员时,编译的时候会自动生成acces$方法,这种情况会导致方法数增多,如果
修改为非private权限就可以去除这个access$方法。(西瓜视频减少了5w左右的方法数,视不同项目决定)
4⃣️java语言中,考虑一些变量修饰符改为非private修饰,可以减少一些set和get方法
类/方法混淆
1⃣️配置proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro',此处采用默认的混淆文本,也可以自定义。使用keep保持不需要进行混淆的文件。
2⃣️配置优化的proguard: proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' (不太稳定,比如在Dalvik模拟器上可能发生闪退,不推荐)
资源混淆
AndResGuard是微信推出的一款开源工具,对资源文件进行混淆,使用的方法很简单,效果显著。
使用方法:https://github.com/shwenzhang/AndResGuard
原理:用更短的名字替换原来的长名字
res/mipmap/icon.png 资源混淆后 r/a/b.png
res/drawable/icon.png ============> r/c/b.png
res/drawable/splash.png r/c/d.png
(同一文件夹被映射到相同的新路径,同样的名字被映射到相同的资源名字)
⚠️白名单:若想通过getIdentifier方式获得资源,需要放置白名单中,否则找不到对应资源。务必将程序桌面icon加入白名单,友盟等,加入白名单。多测试,多测试,多测试!
更多原理可以参考:
https://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=208135658&idx=1&sn=ac9bd6b4927e9e82f9fa14e396183a8f#rd
https://wiki.bytedance.net/pages/viewpage.action?pageId=115947182
使用一套图片资源
除了桌面icon外,其他图片资源考虑只适配一种分辨率的,如只保留xhdpi目录下的资源图
图片压缩
使用tinypng进行图片压缩,可以反复压缩,但是相应解压缩时间变长,带来一些性能影响,对用户体验不会造成影响。
⚠️启动页图片最好不要进行压缩,启动时间会变长
tinypng批量处理
1)安装pip https://pypi.org/project/pip/
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
sudo python get-pip.py
2)安装tinify
sudo pip install --upgrade tinify
3)生成tinypng key 每个月免费500张
对图片进行批量压缩,文件夹大小由1.5M减少到800+k,但是apk的大小实际减少300k左右。因为在打包过程中,本身就会对图片进行一定程度的压缩。
此外,谷歌开发了一款开源工具butteraugli,对压缩质量进行衡量 https://github.com/google/butteraugli
其他图片格式
1⃣️jpg 对于启动图等很大的图片,可以考虑用jpg格式代替png
2⃣️webp格式
支持4.0+设备,4.0以下不显示图片
在4.0 ~ 4.2.1的设备上无法显示带有透明度的webp,比如,把png转成webp则无法显示,但是如果把png先转成jpg再转成webp则能正常显示了,但会丢失透明度。
3⃣️矢量图svg
优点:矢量图由点和线组成,放再大也可以保持清晰度,占用空间小,不会出现锯齿
缺点:只支持5.0+系统,与位图相比多一层计算,消耗更多性能
⚠️android系统对png图片的解析时间是最短的,更换图片格式的时候要考虑一下性能损失。
so兼容一个平台
如删除armable-v7
包下的so,
基本上armable的so也是兼容armable-v7的,armable-v7a的库会对图形渲染方面有很大的改进,如果没有这方面的要求,可以精简。
⚠️不排除有极少数设备会Crash,充分测试,充分测试,充分测试。
精简第三方库
如果第三方库做了拆分,只引用需要的模块,不要引入整个库
如support-v4在24.2.0以后,做了拆分
总结
可以长期使用的方法总结如下
1⃣️2-3版本使用lint清除一次无用资源
2⃣️配置minifyEnable,开启类混淆
3⃣️对新加入的图,使用tinypng或其他工具进行压缩
4⃣️微信资源压缩工具AndResGuard,配置一次,永久生效(注意白名单,注意多测试