如何做到将apk大小减少6M
我们的apk去年业务需求增长迅速,伴随而来的是apk大小由年初的20M以下,增长到年底的30M。
包大小增长的坏处有:
- 过大的下载包会降低新用户的下载兴趣
- 增加下载安装中失败的风险
- 提高各个渠道下载发布的成本(渠道是会根据你的包大小设置收费规则的)
因此,减少包大小的工作看上去不痛不痒,其实还挺重要的。
接下来,我将分享下我在包大小瘦身方面的努力。
技术列表
- lint检查
- 微信资源压缩
- webp本地图片处理
- tiny图片处理
- proguard
lint检查清除冗余
实践方式
Android Studio
Analyze -> Run Inspection by Name
unusedResources
unused declaration
结果:
我们apk里引用了近一百的aar。我对他们进行逐一检查。
发现了300+冗余文件 节省了700+KB字节占用
建议:
各业务团队因养成定期检查的良好习惯。即减少了冗余,又能减少平时开发业务中无用代码的干扰。
微信资源压缩
简单原理介绍:
apk代码运行时,是通过 code ->R ->res找到对应资源的。
而R ->res的映射关系是打包时写在resources.arsc里的。
所以对生成的apk进行的操作如下:
- 先解压缩包
- 然后对res目录下的文件夹和文件进行名称替换
- 同时修改resources.arsc里对应的R与资源的映射关系
- 然后再打包签名生成新的apk
使用说明
命令:
Java -jar andresguard-1.1.jar mogujie.apk -config
weixinResShrinkConfig.xml
输入:
andresguard-1.1.jar
需要处理的apk文件
配置文件config.xml
输出:
处理后的apk
mapping文件,记录名称变动前后对应关系
config.xml配置了
whitelist (com.mogujie.R.XXX.XXX)
sign (签名文件路径和密码)
其他相关处理(如:是否压缩资源,是否用7Z压缩等)
结果:
能将apk包大小减少2M+
处理后的apk资源变成混淆名称,起到一定的安全保护作用
日常维护注意事项:
代码中有通过getIdentifier 获取的资源文件需要添加白名单
推广:
微信资源压缩工具与业务代码无关,我们已经把这部分技术处理整合到打包系统里,推荐大家在发布你们的apk时加入微信资源压缩,效果杠杠的。
附:
微信Android资源混淆打包工具原理:
github地址:
https://github.com/shwenzhang/AndResGuard/blob/master/README.zh-cn.md
webp本地图片处理
背景
Android4.0+原生支持,无需修改代码。
相同图片效果,webp的文件占用空间更小。
解码和渲染时间比png,jpg高出几倍,但用户体验几乎不受影响。
关于webp与png,jpg详细对比评测,可参考腾讯团队webp探索之路:
http://isux.tencent.com/introduction-of-webp.html
wep命令的安装与使用,参考:
https://developers.google.com/speed/webp/
实践
文件夹名称 | 文件夹描述 | 大小 |
---|---|---|
p | drawable-xhdpi-v4里的jpg和png图片 | 1409张 3.8M |
cwebpp | 通过webp有损压缩全部图片后的文件夹 | 1409张 2M |
webpp-lossless | 通过webp无损压缩后的文件夹 | 1396张 3.5M |
经过大量的测试,最后将webp处理设置为:有损,质量75
cwebp -q 75 -m 6 a.png -o a.webp
考虑到drawable-xhdpi-v4以外的文件夹下的图片,webp压缩能减少2M+的apk空间
应用
因为webp的图片格式没有默认的图片浏览器,必须通过chrome浏览器打开,影响了平时业务开发的便捷性。因此,我设计将webp图片处理工作放在了主客build时进行。
gradle build主客时,有一个mergeXXXResource Task,它将主客和aar中所有的res资源统一整合到/build/intermediates/res/ flavorName/ {buildType}目录下。
因此,我写了一个gradle plugin插件 webpConvertPlugin,在mergeXXXResource Task执行后,processXXXResource Task前,插入新的webpConvertPlugin Task 将上述目录下中drawable项目目录里的png,jpg图片(不包含.9图片,webp转换后显示效果不佳)批量处理成webp图片。这样最后打包生成的apk里就是webp图片了。
问题:
在我们上线webp插件时遇到了一个无法规避的严重问题。有alpha值的jpg图片,经过webp转换后,无法在4.0,4.1的Android系统上运行。经过调研从http://developer.android.com/guide/appendix/media-formats.html中找到了答案:
只有在4.2.1+以上的机型,才能解析无损或者有透明度调整的webp图片
然后我又用imagemagick的命令遍历apk中所有图片,统计出1500+张的图片只有80多张图片没有alpha值。
因此,在4.0 4.1目前还有大量用户的前提下,webp本地图片批量替换的方案无法上线。
推广
webp批量处理本地图片的插件适用于各个Android项目,我们开源了自写的webp插件,请参考:
https://github.com/mogujie/WebpConvert_Gradle_Plugin
tiny图片处理
https://tinypng.com/
目前所知图片压缩效果最好的网站。谁用谁知道。
为解决日常开发中,大家手动去此网站挨个处理图片的困扰。我利用tiny提供的jar包做了个批量处理本地图片的tinyPIC gradle plugin。它在build 中插入一个新的tinyPicPlugin task.遍历寻找项目res中以drawable开头的文件夹中的图片资源,调用tiny API进行压缩工作并替换原来的文件。
重要文件
tinypic_white_list.txt(白名单,可设置不进行处理的图片)
tinypic_compressed_list.txt(记录处理过的图片,避免重复压缩)
推广
tinyPIC插件适用于各个Android项目,我们也开源了自写的tiny 插件,接入方法请参考:
https://github.com/mogujie/TinyPIC_Gradle_Plugin
Proguard
Proguard是编译时对java代码进行压缩,混淆,优化,预编译等操作的集成化工具。达到删除冗余,增加安全防护,减小大小的功效。具体介绍,可参考
http://mobile.mogujie.org/doc/android/mixup.html
项目的应用
1. mogu_proguard_annotations.pro 支持proguard注解的使用
(你可以在~/Android/sdk/tools/proguard/examples/annotations/lib中找到配置文件annotations.pro)
2. mogu_proguard.pro 保证功能正常运行的相关配置(根据项目自行配置)
如何写proguard.pro的配置
mogu_proguard.pro的配置主要是为了解决项目在proguard使用中遇到的问题。
遇到的问题又分为两个阶段:
-
编译时的报错 Problems while processing
常见于第三方API的Warning: can’t find referenced class
解决方法如:
-dontwarn com.google.** -keep class com.google.** { *;}
-
运行时异常 Problems at run-time
原因基本都是原本需要具体名字的上下文,混淆后找不到造成的。主要有,native方法失效,注解失效,json数据解析失败,反射失效。
解决方法:
无源代码的第三方API,添加对应的-dontwarn -keep
自有代码使用注解,注解前build.gradle里加入
dependencies { compile "com.infstory:proguard-annotations:1.0.2" }
或在~/Android/sdk/tools/proguard/examples/annotations/lib里找到并引入annotations.jar
你的xxx.pro配置文件里
-keep class proguard.annotation.* {*;}
然后分别按照下面的情况使用注解
-
.so native方法找不到引起的问题
请将有.so native的类加上
@proguard.annotation.KeepClassMembers
-
注解类失效
请将注解类加上
@proguard.annotation.Keep
-
根据写死的或者重服务器传过来的类名称 反射实例化类相关的功能失效
请将需要反射的类加上
@proguard.annotation.Keep
-
json数据解析,数据展示不出来
请将需要json解析的类加上
@proguard.annotation.KeepClassMembers
-
更多关于注解的使用,可参考:https://github.com/yongjhih/proguard-annotations
异常日志的转义
混淆后的错误日志会变成这样
java.lang.NoSuchFieldError: USER_COMMENT
at e.a.a.a(Unknown Source)
为了定位问题代码,需要使用mapping文件。
简单点的日志可直接在mapping中搜索e.a.a.a,获取对应的源文件
复杂点的就要用到proguardgui.jar这个工具了。在你的Android sdk/tools/proguard/lib
目录中可以找到。
因业务代码不断更新,无法统计具体数值,故大致给出每个技术减少的合理效果范围
成果
技术名称 | 减少大小范围 |
---|---|
lint检查删除冗余 | 700kb ~ 800kb |
微信资源压缩 | 2M左右 |
微信webp本地图片处理压缩 | 未上线 |
tiny图片处理 | 0.8M ~ 1.3M |
proguard | 1.8M ~ 2.3M |
总计 | 6M左右 |
微信资源压缩和proguard 提供了Android apk 一定的资源文件和源代码的安全保护能力