APK 优化

项目中对包体积的优化主要分两部分:安装包监控、安装包大小优化。

安装包监控

Android Studio 的 APK Analyser
这是 Android Studio 提供的一个 APK 检测工具,通过它可以查看一个 APK 文件内部各项内容所占的大小,并且按照大小排序显示。因此我们很容易观察到 APK 中哪一部分内容占用了最大空间,并作出优化处理。APK Analyzer 的使用非常简单,只要将需要分析的 APK 文件拖入 Android Studio 中,点开即可。

实际上 APK Analyzer 的作用不光是查看 APK 大小,从它的名字也能看出它是用来分析 APK 的,因此可以使用它来分析一些优秀 APK 的目录结构、代码规范,甚至是使用了哪些动态库技术等。


安装包大小优化

删除无用文件
使用 Lint 查看未引用资源。Lint 是一个静态扫描工具,它可以识别出项目中没有被任何代码所引用到的资源文件。具体使用也很简单,只要在 Android Studio 中点击 Analyze -> Inspect Code,然后选中整个项目即可,如果项目中有未被使用资源,则 Lint 会在窗口 Inspection Result 中显示(Android -> Lint -> Performance)。

使用 shrinkResources 能够在项目编译阶段,删除所有在项目中未被使用到的资源文件。但是需要将 minifyEnabled 选项设置为 true。

使用 resConfig 限定国际化资源文件。有时候我们使用到的三方库中可能会对各种国际化语言进行支持,但是我们自己的项目只支持某个语言,比如中文,那我们可以在 gradle 的 defaultConfig 中使用 resConfig 来限制打包到 APK 中的国际化资源文件,具体如下所示:

1
2
3
4
5
defaultConfig {
...
// 使用 resConfigs 限定支持的语言
resConfigs "zh"
}

文件优化

1、关于静态图片优化

优先使用 VectorDrawable 图片,如果 UI 无法提供 VectorDrawable 图片,那么 webp 格式是一个不错的选择。Android Studio 也支持直接将 png 或者 jpg 格式图片转化为 webp 格式。

2、关于动态图片优化

实际上 webp 也可以作动态图,只是目前对 webp 动图支持的三方库并不多,谷歌官方推荐的 Glide 对 webp 支持也不是很友好。

但是谷歌推出了一套 C++ 依赖库,上层开发人员可以基于此库的基础上使用 JNI 来解析 Animated webp 图片,并将解析出来的每一帧封装成一个 Bitmap,并根据解析出来的时间差值动态显示相应的帧 Bitmap 即可。如果 JNI 不熟或者不想再花时间精力去实现 JNI 调用,可以考虑使用 GitHub 的 Android-WebP 。Android 开发人员只需使用 WebpImageView 控件并指定图片路径即可。

3、关于引入三方库

在 App 中会引入各种三方的”轮子”,但是在引入之前最好权衡一下是否需要将其代码全部引入,造成不必要的代码或者资源也被打包到 APK 中。

比如在我们项目中曾经使用到哔哩哔哩的 ijkplayer 库,原因是我们实现的视频渲染功能,在某些旧的厂商手机中无法正常播放。后来分析下来总结是厂商手机并没有很好地支持谷歌最新的硬编码格式,而使用 ijkplayer 的软编码恰恰能解决此问题。

但是 ijkplayer 是一套完整的视频播放器,很多功能都不是必需的,这种情况下如果只是因为解决一个在很小部分的手机上的 bug,而引入一个比较大的库性价比是不高的,因此需要将 ijkplayer 中关于软编码的功能摘取出来放到项目中。

这种做法同样适用于对 webp 动图的实现方案,上文中有介绍我们使用了谷歌官方推荐的 libwebp,但是这个库不光是为了解析 webp 图片,还有很大一部分代码是为了实现生成一个 webp 图片,这部分代码我们是不需要的,因此也需要将这部分代码给删除,最终编译之后生成的 so 库大小可以减少 1/ 3 左右。

4、关于 App Bundle

如果 App 是海外项目,那么会舒服很多。因为谷歌官方支持动态发布。正常情况下我们的 APK 中为了更好地适配屏幕、语言等,会在项目里添加多套相应的资源文件,比如不同 hdpi 的 drawable,或者不同 CPU 下的 so 文件,最终打包生成的 APK 中会包含所有的资源文件。但是实际上一台手机设备只会用到这其中的一套资源,这无形中就已经产生了一些不必要的资源浪费。而谷歌的 Dynamic Delivery 功能就天然地解决了这个问题,通过 Google Play Store 安装 APK 时,会根据安装设备的属性,只选取相应的资源打包到 APK 文件中。

另外我们在项目中也使用了另一个 App Bundle 中比较好用的选项–Dynamic Asset Delivery。这个功能本来只是针对安装包超过 100M 的 App,但是不影响我们使用这套方案进行安装包优化。具体做法就是将大部分 assets 中的资源使用无损压缩的方式,压缩成一个 .obb 格式的文件,然后每次发布 APK 时都将此 obb 文件设置为 APK 的 bundle 文件,这样也可以减少用户实际的安装包大小。

但是 App Bundle 目前只适合在 Google Play Store 上发布的项目,国内目前还是通过各家的插件化方案来实现动态部署,一定程度上也可以算作减少安装包大小的方案。

5、其它

代码
保持良好的编程习惯,不要重复或者不用的代码,谨慎添加libs,移除使用不到的libs。
使用proguard混淆代码,它会对不用的代码做优化,并且混淆后也能够减少安装包的大小。
native code的部分,大多数情况下只需要支持armabi与x86的架构即可。如果非必须,可以考虑拿掉x86的部分。

资源
开启shrinkResourse(shrink-收缩),会将没有用到的图片变成一个像素点
使用Lint工具查找没有使用到的资源。去除不使用的图片,String,XML等等。 assets目录下的资源请确保没有用不上的文件。
生成APK的时候,aapt工具本身会对png做优化,但是在此之前还可以使用其他工具如tinypng对图片进行进一步的压缩预处理。
jpeg还是png,根据需要做选择,在某些时候jpeg可以减少图片的体积,对于非透明的大图,使用JPG(没有透明度信息),代替PNG格式。 对于9.png的图片,可拉伸区域尽量切小,另外可以通过使用9.png拉伸达到大图效果的时候尽量不要使用整张大图。
使用webp图片格式,进一步压缩图片资源

策略
有选择性的提供hdpi,xhdpi,xxhdpi的图片资源。建议优先提供xhdpi的图片,对于mdpi,ldpi与xxxhdpi根据需要提供有差异的部分即可。
尽可能的重用已有的图片资源。例如对称的图片,只需要提供一张,另外一张图片可以通过代码旋转的方式实现。
能用代码绘制实现的功能,尽量不要使用大量的图片。例如减少使用多张图片组成animate-list的AnimationDrawable,这种方式提供了多张图片很占空间。
使用第三方包时把用到的代码加到项目中来,避免引用整一个第三方库
删除无用的语言资源(删除国际化文件)

其它

  • 将图片转换为 webp 格式
  • 去除多语言
  • 去除不必要 so 库
  • 去除无用资源 Link 检查(谨慎删除)
  • 开启混淆
  • 移除无用资源 shinkResource
  • 开启删除无用资源 (严格模式和普通模式)
  • AndResGuard 微信资源压缩
build.gradle(:app)
1
2
3
4
5
6
7
8
9
10
11
buildTypes {
release {
// 清理无用资源
shrinkResources true
// 是否启动 ZipAlign 压缩
zipAlignEnabled true
// 是否混淆
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

离线资源处理

  • 离线资源建议从服务器下载,不必打入 APK 包中。

备注

参考资料:

拉钩教育-Android 工程师进阶 34 讲

单词音标: