业界方案
在网上随便搜索一下就能发现瘦身有好多方案,但是实践一下就能发现好多都不靠谱
方案 | 作用 | 瘦身效果 |
---|---|---|
proguard | 代码混淆 | 效果明显 |
abiFilter "armeabi" | 去除其他平台so | 效果明显 |
resConfigs "zh" | 语言文件去除 | 0.1M |
shrinkResources | 无用资源去除需维护keep文件 | 1M |
TinyPng | 图片压缩,账号收费 | 3M |
ThinR | 移除R文件 | 0.3M |
AndResGuard | 资源混淆白名单维护难 | 资源混淆0.3M,7zip压缩2M |
webp | android兼容性差 | 不推荐 |
Lint 无用资源去除 | 有可能删除getIdentifier调用的资源 | 不推荐 |
redex | 安全风险高,对于加固、热修复等功能有影响 | 未实践 |
so动态加载 | 风险高,大部分so都需要实时加载 | 未实践 |
加固 | 隐藏dex | 1M |
重复资源优化 | 对比资源文件 md5,删除重复文件和resources.arsc 中的定义 | 0.2M |
移除TINY_PNG文件 | 通过android-chunk-utils把resources.arsc 中对应的定义和文件移除,风险高 | 美团文章一带而过,我实践一下,实际代码特别复杂,arsc 文件索引value要重新计算,减小0.1M都不到 |
方案实践
Smallapk Gradle插件减小APK体积25%
动态资源查找
其他方案网上都有,我重点讲讲SmallApk插件怎么解决getIdentifier方法带来的动态资源问题。
ShrinkResources只能去除小部分无用资源的问题
解决AndResGuard需要配置白名单的问题
首先需要了解ShrinkResources的原理:
通过ResourceUseModel建立一个资源引用树,找到有可能是resource.getIdentifier调用的资源标记为reachable,找到无用资源并替换成tiny的小文件
用这种方式查找到的动态资源会特别多,因为用正则表达式匹配了所有的字符串,那么如何精确找到动态资源呢,你会发现android源码里面写着Todo,哈哈。
那就只能自己想个方案找到getIdentifier引用的所有资源了。
先来看看效果,这个是getIdentifier的多种调用方式
这个是用SmallApk插件找到的动态资源
这个是找到的动态资源调用关系图
那么SmallApk是怎么做的呢
思路和android源码ResourceUsageAnalyzer是一样的,都是匹配字符串常量,唯一的区别就是加入了方法有向图搜索节点,排除大部分无用字符串。
首先形成调用有向图
接着找到getIdentifier的方法节点
然后找到所有调用getIdentifier的字符串常量
最后匹配字符串常量找到动态资源
找到动态资源以后就能去解决AndResGuard和ShrinkResources的问题了
解决ShrinkResources只能去除小部分无用资源的问题,只要把找到的动态资源文件写入到/build/intermediates/res/merged/release/raw/keep.xml
中
解决AndResGuard需要配置白名单的问题,只要把动态资源加入到白名单就可以
你问我答
AndResGuard会混淆资源文件名,xml资源文件里面也使用了文件名的字符串,那为什么apk没有崩溃?
因为编译完以后布局xml文件里变成了int常量,AndResGuard修改的是字符串,int索引没变proguard也会去除R文件,那为什么用ThinR还会减小包体积?
因为aar包里不存在R.class的,app打包的时候会重新生成lib库的R文件,但是因为生成lib库的class文件时R文件的变量不是final,所以aar里面是直接引用引用了lib.R.id,
然后proguard判断lib库R文件是有引用关系的不能去除,ThinR相当于接着把lib库里面的R文件删除在mac上解压缩apk再压缩会去,你会发现这个apk已经没法安装了,为什么,照理说不做任何操作应该不影响apk签名呀?
因为MAC解压缩的时候会存在.DS_Store文件,直接压缩会把外面的文件夹目录也压缩进去重新压缩apk以后体积会小,为什么apk自己不是压缩过了吗?
因为默认图片是不压缩的shrinkResources不是删除了无用资源吗,那为什么我用Lint去删除无用资源,包体积还是会变小?
一个是资源问题,一个是代码问题。
资源问题:shrinkResources匹配字符串常量得到的无用资源会比较少,而lint扫描会只扫描硬静态引用资源,这样扫描的资源文件会比较多
代码问题:lint还会删掉java文件,而shrinkResources只会去除无用资源,虽然android源码里面二次打包TWO_PASS_AAPT
,但是默认没开启android gradle插件默认是开启v2签名的,为什么在我们的app里面用修改meta-inf文件的方式加入渠道号还可以运行?
因为我们先加固,然后重新v1签名,再打渠道包,运气好,刚好绕过了v2签名的坑,哈哈zipalign会影响v1签名和v2签名吗?
请在v1签名后使用zipalign,v2签名前使用zipalign,v1签名和v2签名可以同时存在,不能只用v2签名,因为在7.0手机只会校验v1签名
作者:sunshine8
链接:https://www.jianshu.com/p/e853c0467775
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
评论