Android Apk廋身完整总结
一.目的
- APK在安装和更新之前都需要经过网络将其下载到手机,如果APK越大消耗的流量就会越多,特别是对于使用移动网络的用户来讲,消耗流量越多就代表需要花更多的钱去购买流量。同时一些第三方应用商城也会对上传的APK大小有限制,所以为了能够让产品能够更受商城和用户欢迎,APK瘦身是第一步,更小的APK标示着更多地用户愿意去下载和体验。
二.问题排查
安装包的组成
要对安装包做瘦身,首先需要了解安装包的组成结构,这里简单的梳理了一下组成各个部分及其作用:
其中,在安装包中占比较大的包括:dex文件、res文件夹、assets文件夹、lib文件夹以及resource.arsc文件。所以,接下来的瘦身优化就是让这些文件变小,以此达到瘦身的目的。
在 Android Studio 2.2.3 开始,就加入了浏览 APK 结构的功能,我们直接把安装包拖入 IDE ,就可以直接浏览其组成和对应大小,这样能够很方便的对比分析出每一步优化后的结果。
1.分析so库49.2M打包成APK占用22.6M,占比67.6
2.dex11M打包占用4.3M,占比12.9
3.资源文件7M打包6M,占比17.9
上图分析出so、dex、资源文件总占比98.4,着手从这三点进行优化。
三.优化建议
- 资源瘦身
a) 尽量只保存一份图片资源。开发目录下会有个mipmap 目录用于适配不同 dpi 的屏幕,下面是不同命名目录所适配的 dpi 范围
目前市面上绝大部分机型都处于 xxhdpi 的适配范围,所以可以考虑只保留 xxhdpi 目录下一份图片资源,具体保留哪个目录下的资源和保留几份资源还得依照应用自身的实际机型分布决定。
b) 使用 Drawable XML、Color、.9 PNG 代替 PNG
- 一些情况下,我们可以考虑使用 Drawable XML 来代替 PNG,如:渐变的背景图,用几行 XML
就可以描绘出来,何必使用几十到上百K的 PNG 文件; - 用 Color 代替 PNG,如:纯色的背景;
- 从性能上看,比起使用图片资源需要先将其生成 Bitmap 再传到底层交由 GPU 渲染,用 Drawable XML 和 Color 则更加高效,它是直接将 Shape 信息传到底层由 GPU 进行渲染,CPU 和 内存的占用会更少;
- 用 .9 PNG 代替 PNG,场景很多,不举例了;
c) 使用 JPG 代替 PNG
- 用 JPG 代替 PNG,由于 JPG 没有 Alpha 通道,所以文件更小,适用于不需要透明度的图片可以考虑。
d) 可以使用shape作为背景
- 很多点击效果可能会使用到图片,可以换成shape是实现。
e) 使用vector
- 每个drawable都有相应尺寸的图标,占了不少大小,后来部分用vector代替了图片。注意一点,Button在xml里设置app:srcCompat居然无效,只能在代码里设置。
button.setImageResource(R.drawable.ic_favorite_border_black_24dp)。
f) 谨慎使用 WebP 代替 PNG
由于 WebP 效果好,且相同效果下, WebP 文件比 PNG 文件要小得多。
WebP 在 Android 端,最低只支持 4.0 ,要兼容 4.0 以下的环境需要额外引入兼容库,反而增大安装包体积;
解压了 BAT 们的应用,以及同类竞品,基本没有发现在资源文件中用 WebP 的;
g) 有损编码格式的音频文件代替无损格式的音频文件
从下面这篇官方文档
(https://developer.android.com/guide/topics/media/media-formats.html)
可以看到 Android 平台支持的音视频格式,下面列出有损和无损常用的格式(不要认为有损编码就是音质很差):
无损格式:WAV,PCM,ALS,ALAC,TAK,FLAC,APE,WavPack(WV)
有损格式:MP3,AAC,WMA,Ogg Vorbis
实际开发中需要使用音频文件尽量采用 MP3、Ogg 这种有损格式,尽量不要用 WAV、PCM 这种无损音频。
h) 移除无用的资源
这里的移除无用资源文件主要分为两个部分:不打包没有使用的资源和删除没有使用的资源。
- 不打包没有使用的资源,在项目的 build.gradle 中配置 shrinkResources true 即可。
-
开启minifyEnabled混淆代码,在app/build.gradle打开minifyEnabled为true
- 如果APP支持中文,可以配置resConfigs,只支持中文
-
删除没有使用的资源,通过 Android Studio 选中项目右键 => Analyze => Run Inspection by Name => 输入 Unused resuroces
i) 工具
TinyPNG:https://tinypng.com/ ,支持对 PNG/JPEG 文件做压缩处理,效果不错。
pngquant:https://pngquant.org/ , 支持 PNG 压缩,有时候 TinyPNG 处理过的图片噪点会稍多,可以考虑用 pngquant 来处理。
ImageOptim:https://imageoptim.com/mac ,支持压缩 PNG/JPEG/GIF ,而且效果显著,可以看看这里 https://www.diycode.cc/topics/496 ,遗憾的是它只支持 Mac ,Windows 党很难过。
mozjpeg:https://imageoptim.com/mozjpeg , 用于 PNG 转 JPEG、JPEG 压缩,效果很好。
Adobe Audition CC:http://www.adobe.com/cn/products/audition.html ,Adobe 出品,支持对音频的采样率,分辨率和声道数目做更改,以此达到裁剪音频的目的(采样率,分辨率和声道数目是音频文件格式的关键参数,决定着音频文件的大小)。
使用微信Android资源混淆工具(https://github.com/shwenzhang/AndResGuard)
AndResGuard打包命令行:gradlew resguardRelease,最终的混淆APK会生成在{App}/build/output/apk/AndResGuard目录下。
j) 基于dex廋身
- Facebook出品dex优化框架ReDex,启动速度提升 20% 以上,Dex 大小减小 25%,对于内存较小的机型启动速度的优化效果尤其明显,目前只支持Mac平台、Linux平台。
k) Native库瘦身
-
Native 库瘦身主要是减小对 CPU 架构的支持,配置起来很简单,在 build.gradle 使用 abiFilters 配置需要用到的 CPU 架构,并将不需要兼容的 so 文件从项目中移除即可。我这边最终只保留了对 armeabi-v7a 支持。
综上所述,就可以有效的精简我们安装包中的 lib 文件夹大小,从而达到瘦身目的。也有一种做法是通过在 build.gradle 配置 include 来针对每个 CPU 架构生成单独的安装包,虽然看起来很不错,但是很多国内应用市场上架的时候并不支持这种每个 CPU 配置一个包的做法,所以此做法较为鸡肋,不太建议去做,如果应用只上 Google Play ,那确实要比配置 abiFilters 好得多。
l) 代码瘦身
这里可以做的事情也是很多,主要如下:
移除废弃功能的代码,反正有AS ,删了代码随时可以找回;
移除重复的代码,如:已经有了的功能代码,团队成员不知道自己又写了一套,只能靠代码 Review 解决了;
移除功能重叠的框架,如:项目中有几套网络访问框架 Volley、Retrofit 等,同样只能靠代码 Review 解决;
移除无用的 dependencies 或者 jar 包;
减小对 Support 兼容包的依赖,Support-V4 包非常大,项目引入无疑会增大 dex 文件的大小,Google 已经意识到这个问题,所以 Support-V7 一开始就做了拆分,并且开始对 Support-V4 做拆分,虽然目前成果还不明显,不过还是蛮值得期待的,特别是发现你少了 Support-V4 包后,可能就从2个 dex 变成1个 dex 了呢;
插件化,一种懒加载思想的体现,先让用户能够安装宿主包,对于一些功能模块做插件化,在特定的时机再下载安装;
参考:
1. http://wuxiaolong.me/2017/03/19/ReduceAPKSize/?nsukey=52PBvRoPKrEz6vkSDHnnP1%2B%2FW5qJfm%2FOnA%2B1hvysYEEaCVd0IEgOQaWcv4Dc3HzKWh92wlOskWJ3ZXDPnHqhvsPU2d8ALMkpaJ54l5StPASm8bFZejhB80UIBcedUDia7GMYSEbOPnJ4FVE3OPCiVK5LinXoZRAR%2BsWA2J%2F04ipHrD5rVr1QLes9kY4snbaZ