WebP局限性
在我的上一篇文章提到,让大家谨慎使用WebP格式的图片,并且提及到Android
Studio不支持预览WebP图片的问题,而最新的2.3版本已经支持预览WebP文件,同时还能够帮我们将JPG和PNG转换成为WebP图片,虽然IDE的升级让我们喜出望外,但你仍然要关心WebP的一下几个问题:
WebP的编码时间为PNG的5倍以上,解码速度与PNG差不多,甚至更快;
WebP编码时占用内存比PNG高25%,解码时比PNG低30%;
Android 4.0+开始支持WebP图片,但是带透明度的WebP图片是在4.2.1+之后才支持的,在4.2.1之前则无法显示;
针对以上一二点,你可能需要考虑一下自家用户实际机型分布情况,如果大部分都是中低端配置机型,使用了WebP后对体验可能会有所下降,对于高配设备,完全可以忽略。而针对第三点,则需要考虑用户的系统分布,相信绝大部分App的用户已经满足4.0+这个条件,但对于满足4.2.1+条件就很难说了,至少我们这款有几百万用户的产品仍有不少用户不满足此条件,因此也不敢随便提升最低兼容版本,但是你仍然可以进行一些WebP的处理,主要如下:
对于不需要透明度的PNG图片,先转成JPG格式后,再使用IDE进行WebP转换。不要直接用PNG转换成WebP,否则转换后的WebP仍然带了透明度,在4.2.1之前的设备任然无法显示,先转换成JPG就是为了去除PNG的透明度;
将PNG转换成JPG时,建议采用专业的图片处理工具,因为我在使用一些在线转换工具转换成为JPG时,发现有少量的图片转换成JPG后再转成WebP时在4.2.1之前依然无法正常显示,但经过专业的处理的图片处理工具转换后的图片则全部没有问题;
在将JPG转换成WebP图片时,采用IDE默认75的有损压缩质量,超过75之后,压缩效果反而会变差;
虽然IDE给了我们WebP更方便的支持,但实际上受限于Android系统版本的问题,所以,我们只能对有限的图片做处理,对于项目中有大量留白的透明像素的图片,转换成JPG效果会失真,所以,也只能暂时忽略掉。最后,推荐一个腾讯出品的WebP在线工具——智图:https://zhitu.isux.us/。关于WebP的知识,我非常建议大家阅读以下两篇腾讯出品的文章,你会收获很多
shrinkResources误区
网上很多文章都说在build.gradle中开启shrinkResources true的设置,这样可以在打包过程中工具链会自动帮你移除相应一些无用的资源。
而实际上shrinkResource只能帮你压缩合并资源,并不能帮你移除资源文件,读者可以自行拷贝一些无用的资源文件放到工程中打包验证,你会发这些文件将会原封不动的打进你的安装包中。不过,从另一个角度去想,可能是因为我们拷贝资源到工程中时,IDE会帮我们在R.java文件中生成对应的资源ID信息,所以即使没有显式的在代码中引用资源,但是自动生成信息的R类却一直引用着。依靠配置shrinkResources true移除资源的目的无法达到,我们只能通过IDE的Remove Unused Resources功能筛选出工程中对应无用的资源
不过,建议大家使用它时选择Preview模式,不要直接使用Refactor模式
因为代码中引用方式不同,所以存在代码中使用了某个资源,但是依然被当成没有引用的情况,例如通过使用getIdentifier、Uri等方式。
resConfigs
Google在官方文档提到,可以通过限制语言资源来压缩包大小,但实际操作上,由于我们的应用原本就只支持中英文,所以实践之后收效甚微,这里简单提及一下。
SO压缩
按照动态加载的思想,我们对项目中很多音视频处理的SO文件进行了7zip极限压缩,等到用户升级安装应用时,才动态解压出SO文件进行加载调用(解压耗时在我们最差的机型上只用了2.8s)。如果你的项目中有也存在大量自己开发的SO文件,可以考虑用这种方案进行优化,成效会很明显。更有甚者,对于不常用的功能需要用到的SO文件,我们可以考虑从网络中下载完成后再动态加载进来,这将更进一步的缩减了你安装包的大小。
源码梳理
我在上一篇文章中提到,对源代码进行梳理建议,不少同学给我留言,基本都是认为源码精简对应用的瘦身作用非常有限。实际上,这并不能一概而论,得看具体的项目情况而言,这里就分享一下我在项目中实际操作好了。
合并网络请求管理,移除原有的android-async-http、Volley,仅保留OkHttp;
合并图片加载管理,移除原有的其他加载管理方案,仅保留Fresco;
合并整理功能重复的一些自定义控件;
移除历史悠久已经废弃的功能模块代码;
裁剪Fresco,例如不需要加载gif,WebP等可以把相应的功能模块移除,不需要用OkHttp来管理Fresco的网络加载可以移除(实际上移除后发现反倒少了一些Fresco加载图片效果出问题的一些BUG,意料之外的收获);
移除一个开源的JSON解析库com.fasterxml.jackson.core:jackson,这货整整8000多个方法,移除后包直接小了200多K;
移除Google Zxing库;
提取Android 4.2.2系统源码中的一个汉字转拼音的类,替换原有项目中的实现方案以及其带进来的一个200多k的附属文件;
当然有些功能比较复杂又加上年代久远,所以梳理还是需要小心谨慎,最好能对一个功能模块了解全面了再进行整理。在整理完源码之后,再清理一波无用的资源,又可以节省不少空间。最后,推荐大家一个计算项目编译后的方法数的Gradle插件
https://github.com/KeepSafe/dexcount-gradle-plugin/
使用它你可以看到每次改进后效果,也可以用它分析出包中含有较多方法的包是哪一个,是否有必要进行排查优化等等。在debug编译的时候注意要开启混淆,这样可以看到实际混淆优化后的效果,因为混淆优化后,会有不少没用的方法被优化移除,但对于引用的第三方类库以及一些混淆规则中keep住的类则原封不动。
插件化
插件化同样是一个不错的优化方案,同样利用动态加载的原理,不过这又属于另一话题了,此处就不铺开详述了。
AndResGuard
https://github.com/shwenzhang/AndResGuard/
来自微信的资源混淆优化方案,同样也是利用了7zip极限压缩,具体原理可以详细查看项目说明,一些需要注意问题均有详细说明。比较值得注意的有以下几点:
项目对于资源文件的名字做了混淆优化,所以一些第三方SDK以及项目中使用getIdentifier、Uri等方式加载资源,需要将对应的资源加入到白名单中,否则会出现闪退;
部分手机桌面快捷图标的实现有问题,务必将程序桌面icon加入白名单;
通过修改zip摘要的方式生产渠道包,出渠道包时,解压重压缩会破坏7zip的效果,通过repackage命令可用7zip重压缩;
实际操作中,我们使用此方案后包只少了大约200K,而且还伴随着一些潜在风险,所以并没有引入使用,其他同学可以实践对比下,再评估是否要使用。
Redex
https://github.com/facebook/redex
Facebook出品的字节码优化方案,其原理是优化打包过程中Java字节码文件,和Proguard的原理一样。针对此方案的优略之处,大家可以看看Trinea的文章:Facebook App优化工具ReDex优化的6点及未优化的一大方面。在实际操作中,使用此方案后包同样也只少了大约200K,并且翻看了Github上的Issue还存在一些莫名其妙的闪退问题后,最终没有考虑引入此方案。
总结
好了,本篇关于Android应用瘦身的文章到这里就结束了,整个实践的过程有颇多的细节需要注意,避免瘦身后又给自己挖了一个大坑。尽量每做一个小模块的改动,就生成一个commit,这样发现问题后可以方便定位回滚。很多同学在我上篇文章中讨论,关于这样的瘦身是否有意义,对于我们的产品来说,经常有很多线下运营活动需要人们现场下载,这样的瘦身对线下活动提高转化率还是非常有用的。