1Android APK瘦身

来源:互联网 发布:自拍神器软件下载 编辑:程序博客网 时间:2024/06/04 18:19

1 APK瘦身的价值

应用的频繁迭代和功能的不断完善使得应用体积迅速增长,同时也促使开发者采用多种手段减小应用的包体积,其可带来两方面价值。
经济价值:通常用户在下载应用时会特别关注APK的大小,特别是当用户处在非WiFi网络环境下载应用,必然会消耗更多的流量和增加更多的费用。
用户体验价值:APK的大小还会直接影响安装速度和加载速度,其对内存的使用以及手机资源的消耗在配置不那么高的设备上体验极为明显。

2 APK组成结构

在使用一些很酷的方法来减少应用程序的大小之前,必须先了解实际的APK文件格式。简单地说,APK是一个包含文件/文件夹的压缩文件。作为一个开发者,我们可以很容易的通过打开压缩文件的方式查看到APK里面的内容。

a) 7zip打开APK后的视图:
这里写图片描述

b) 各个文件或者文件夹的功能:
这里写图片描述

3 APK瘦身方案

通过上面的分析,已经了解了APK的基本构成。下面我们就采用多种手段进行APK瘦身
3.1 针对整体优化
3.1.1 插件化
从应用功能扩张的角度看,APK包体积的增大是必然的,然而插件技术的出现很好的解决了这个问题。通过分离应用中比较独立的模块,然后以插件的形式进行加载,从服务器下载,然后以插件的方式加载到我们的主工程。

3.1.2 7ZIP压缩
一般情况下面,AS直接编译生成的APK里面,.arsc文件是没有进行任何压缩的,前文中APK组成部分的第一张图就可以看出。下面,我们来解压APK,重新用7zip进行压缩,就会发现几乎所有文件都变小了,特别是.arsc文件,减小的比较多。
这里写图片描述
对比7zip压缩前和压缩后APK里面文件的变化,可以看出通过7zip压缩,.arsc文件大概减小了2M多,其它文件/文件夹体积也减小了5%左右。

3.1.3 签名方式
Google在Android7.0系统提供了新的apksigner签名工具,相比使用java提供的jarsigner签名工具,APK体积可以减小约5%(依赖文件数量)。
这里写图片描述
上图中间是未签名的APK,左边是jarsigner签名的,右边是apksigner签名的。
对比未签名的APK,用jarsigner签名工具签名,APK里面所有压缩后的文件和文件夹体积都增大了;而apksigner签名工具签名,除了META_INF文件夹增大了以外,其它文件和文件夹的大小都没有改变。
新的apksigner工具已经集成到Android 7.0 SDK中了,使用方法可以参考官方文档:https://developer.android.com/studio/command-line/apksigner.html。

3.2 针对资源优化
上面描述了对整体文件的优化,下面我们来针对资源文件进行优化。
3.2.1 移除重复的资源
a) 一套资源
Android在适配图片资源的时候,如果只有一套资源,低密度手机会缩放图片,高密度手机会拉伸图片。我们利用这个特性,存放一套资源图就可以供所有密度的手机使用。综合考虑图片清晰度,静态大小和内存占用情况,一般采用xhdpi下的资源图片。
b) 重复资源
很多时候,随着工程的增大,以及开发人员的变动,有些资源文件名字不同,但是内容却完全相同。我们可以通过扫描文件的MD5值,找出名字不同,内容相同的图片并删除,做到图片不重复。

3.2.2 移除无用的资源
由于项目的迭代以及UI改版等各种因素,会导致工程项目里面有许多无用的资源的存在,定期扫描处理无用资源。
a) 通过Lint工具扫描工程资源
当Lint工具扫描发现无用资源的时候,会输出如下的信息,就可以删除这种资源。
这里写图片描述
需要特别注意的是,需要确保不存在反射,资源拼接等访问这些资源,才可以安全的删除掉这些资源,从而减小资源个数。
b) 通过Gradle参数配置
如果工程比较大,由主工程和多个子工程组成的话,子工程里面也可能包含很多的无用资源。可以通过设置shrinkResources=true让Gradle移走无用的资源,否则默认情况下,Gradle编译只会移除无用代码,而不会关心无用资源。
这里写图片描述
需要特别注意的是shrinkResources依赖于minifyEnabled,必须和minifyEnabled一起用,即打开shrinkResources也必须打开minifyEnabled。

c) 通过开源扫描工具
大家可能会发现Lint不是非常好用,当工程里面存在反射,过滤结果非常麻烦。所以我们实现了一个资源扫描的工具(https://github.com/zhuzhumouse/ScanUnusedResouce ),可以过滤掉通过反射调用的资源。原理就是把所有java和xml文件以字符串扫描到内存,然后拿到资源文件(xml,png,jpg等)名称做匹配查找,如果没有匹配到,该资源就是无用资源,可以直接删除。
该扫描工具可以解决反射调用的问题,但是不能解决资源拼接的问题,还有就是不能处理存在很多资源前缀相同的情况。

3.2.3 png图片压缩
可以通过使用图片压缩工具对png图片进行压缩,压缩效果比较好的工具有pngcrush,pngquant,zopflipng等,可以在保持图片质量的前提下,缩减图片的大小。还可以通过网站对图片进行压缩,如比较有名的www.tinypng.com,该网站对上传的图片自动选择合适的压缩算法,压缩比比较高,但是只支持500张免费图片,更多图片处理是要收费的。

3.2.4 采用WebP格式
WebP是Google推出的影像技术,Google大力提倡WebP在网页上进行应用,近两年在Android应用中也开始推广使用。
目前4.2及以上的手机系统已经支持WebP的无损和有损压缩,但是4.0,4.1的手机系统支持只支持不含透明度的有损压缩。如果应用支持的最低版本是4.0,那么就只能针对不含透明度的图片进行WebP转换了。
在Android Studio 2.3版本及以上,我们可以选中 drawable 和 mipmap 文件夹,右键后选择 convert to webp,将图片转为 WebP 格式。如果Android Stuido版本比较低的话,可以直接通过官方提供的cwebp工具,将png转化为WebP。

取两张比较大的图片转化为webp格式进行测试,压缩效果如下:
这里写图片描述
然而不是所有的图片都有这么高的压缩比,WebP主要针对色差比较小的图片,压缩比比较高,有些图片可能压缩后反而会增大,所以任何一种压缩算法只是针对某种类型的图片压缩比比较高,没用万能钥匙的。

3.2.5 优化库中资源
通常在大型的项目中,都会引入很多系统库和第三方的库。比如低版本兼容库V4、V7、网络请求库、图片处理库等,如果库中包含一些大图,而我们并不会用到,就可以采用1x1的透明图片替代,达到既能编译通过,又可以缩小库体积的目的。

3.2.6 大背景图处理
对清晰度要求高的大图片,采用单纯的压缩方法就不能满足UE的要求了,需要找到一种非压缩方式来解决这个问题。纯色图+后台下载的方式很好的解决了这个问题,客户端先使用纯色图片,然后大图从后端下载,这样只是启动的前几次使用纯色图,以后都会使用大图。

3.2.7 Lottie动画库的使用
动画,尤其是帧动画,一直都是相当占用资源的。现在可以通过Lottie动画库,直接用json文件来描述动画,然后直接加载绘制出来。

3.2.8 其它资源策略
A)优先使用9图
B)考虑采用矢量图svg和VectorDrawable
C) 尽量避免使用图片,比如可以使用shape代码实现
D) 尽量使用一张图,如果同一张图,只是旋转角度或者颜色不同,可以通过代码实现
E)lottie

3.3 针对代码优化
上面已经详细的介绍了资源文件的优化方法,通过这些优化,包体积得到明显的缩减,下面我们再来讨论一下代码的优化。
3.3.1 代码混淆
在gradle使用minifyEnabled进行Proguard混淆的配置,可大大减小APP大小:
这里写图片描述
尤其需要注意的是:在proguard中,是否保留符号表对APP的大小是有显著的影响的,可酌情不保留,但是建议尽量保留用于调试。

3.3.2 无用代码扫描
同无用资源扫描方式一样,可以针对无用的代码进行扫描,这里需要关注的一点就是在插件里面通过反射的方法调用的主应用的一些类和方法是不能删除的。
也可以使用SonarQube扫描无用类,以及不同类里面的重复代码。详情请参考:https://github.com/SonarSource/sonarqube 。

3.3.3 剔除R文件
随着项目中资源的增加,会发现生成的dex文件里面R.class文件越来越大。我们知道真正使用资源的地方都是以R.xxx.xxx这种方式访问的,而R.xxx.xx是对应于.arsc文件里面的一个常量值。
arsc里面的内容具体如下:
这里写图片描述
通过这两张截图我们可以看出,直接用ID替换资源访问代码R.XXX.XXX,这样R.class文件就没有任何作用了,可以删除它,并且代码里面的资源访问字符串也变成了常量,两个方面都减小了dex的大小。剔除R文件可以参考开源工具:https://github.com/meili/ThinRPlugin

3.3.4 注解替代枚举
谷歌官方一直强烈推荐用注解替代枚举,一方面可以缩减包体积,另一方便可以节省内存开销。我们来对比一下,在使用注解和使用枚举两种情况下,生成的class文件内容。
这里写图片描述
通过对比可以看到生成的class文件里面,每个变量都是一个对象,并且还有一个value对象数组。
这里写图片描述
注解生成的class文件只是一些常量。

通过上面的代码对比可以看出,常量+注解的形式,一方面可以减小生成的class文件的字节数,另一方面可以减小内存开销。

3.4 .arsc文件优化
在剔除R文件小节中,大家已经看到了.arsc文件内容格式。在整体优化小节中,已经对.arsc进行了比较大的优化,接下来分析一下其它优化方式。
可以采用混淆来缩减资源文件的名称,以及移除未使用的备用资源等方式来优化.arsc文件。如何移除未使用的备用资源,gradle里面增加如下配置:
这里写图片描述

3.5 lib目录优化
只提供对主流架构的支持,比如arm,对于mips和x86架构可以考虑不提供支持,系统会自动提供相应的兼容

4 瘦身过程中遇到的问题

a) WebP支持问题
WebP图片的转化过程中,一定要注意资源拼接的情况。比如如果存在vip_1,vip_2,vip_3,vip_4,vip_5等五个资源,要么都转化成WebP,要么都不转,不能处理其中的一部分。
替换一些引导图的时候,一定要打包工具和客户端同时替换。如果客户端把引导图替换成了WebP格式,而打包的时候,由于不同步,该图片又被替换成png格式,就会导致资源加载不成功,进而程序崩溃。
b) 签名方式
使用apksigner签名工具前,必须先执行zipalign操作;而使用jarsigner签名工具则是先签名,然后再用zipalign优化。

5 参考资料

1、 Reduce APK Size
2、 Shrink Your Code and Resources
3、 Create WebP Images
4、 WebP文件压缩
5、 png文件格式
6、 Facebook工程师是如何改进他们Android客户端的
7、 剔除R文件
8、 图片压缩神器tinypng
9、 pngquat 图片压缩

原创粉丝点击