一、如何查看 APK 的组成
如果要优化 APK 的大小,我们首先需要知道我们编译出来的 APK 都包含哪些东西,然后针对占用大的做裁剪,或者删除不需要的东西,从而达到瘦身的目的。
查看 APK 的内容占用情况很简单,打开 AS ,把 APK 拖到 AS 里面就可以查看 APK 包含的内容了。
通过Android Studio自带的Analyzer进行APK的分析。使用方法:
1、将一个apk拖动到Android Studio的编辑器窗口
2、在Project窗口中,双击build/output/apks/目录下的apk
3、在菜单栏中选择选择Build > Analyze APK,然后选择要分析的apk
分析问题,发掘优化点
从上图Analyzer可以发现,一个APK主要包含如下目录:
-
lib:包含了一些区分于处理器的编译代码,主要是SO文件,一般里面包含很多子目录,例如armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, and mips。
-
res:包含了一些不会被编译到resources.arsc的资源文件。如drawable文件、layout文件等。
-
assets:包含了一些通过AssetManager能够检索到的资源。如MP3、字体、webp等资源文件。
-
META-INF:包含了CERT.SF和CERT.RSA签名文件,还有MANIFEST.MF文件。
除此之外,还包含了如下的文件: -
resources.arsc:包括了所有可以被编译的位于res/values/目录下的XML资源。打包工具在打包过程中会把XML的内容编译成二进制的形式,亦或者把相关资源的引用路径编译成二进制,然后整合到该文件里面。例如string文件、layout的路径、图片的路径等。
-
classes.dex:包含了所有的Java文件编译后的class文件,class文件最终转化成该dex文件。一般文件都比较大,有的App有几个dex文件,这是因为单个DEX文件限制方法数在65536,所以当代码量过大时,就需要通过multiDex进行分包,拆分成多个dex文件,解决这个问题。
-
AndroidManifest.xml:整合了多个module的AndroidMainifest文件的权限、声明等配置到该文件。
知道了APK的组成部分,那么我们就可以针对这些文件/文件夹进行针对性的优化,每个App都不一样,但是方法都是大同小异,本文讲述瘦身策略也是针对这些目录和文件进行优化,这样可以显得更加有条理性。可以看出占大头的是 res 代码等,所以瘦身可以从这三个方面来考虑。
1)资源:无用资源删除、资源混淆。
2)So:只保留 Armeabi、更优方案。
3)代码:Proguard、统一三方库、无用代码删除。
二、如何减少 res 资源大小
① 删除冗余的资源
一般随着项目的迭代,部分图片等资源不再使用了,但是可能仍然被编译到了 apk 里面,所以可以删除这部分不再使用的资源,可以使用 lint 工具来搜索项目中不再使用的图片等资源。
② 重复资源的优化
除了有冗余资源,还有些是文件名不一样,但是内容一样的图片,可以通过比较 md5 值来判断是不是一样的资源,然后编辑 resources.arsc 来重定向。
③ 图片压缩
未压缩的图片文件占用空间较大,可以考虑压缩未压缩过的图片来瘦身。常用的工具是 tinypng 网站。同时也可以借助 TinyPngPlugin 等插件或者其他开源工具来帮助压缩图片。
④ 使用lint删除无用资源
在多人开发过程中,通常都会有漏删无用资源的问题,图片资源也不例外,例如需要删除一个模块的代码时,很容易就会漏删资源文件,所以可以定期使用lint检测出无用的资源文件,原理这里不作介绍,使用方法非常简单,可以直接在AS里面使用,如下图所示。注意:lint检查出来的资源都是无直接引用的,所以如果我们通过getIdentifier()方法引用文件时,lint也会标记为无引用,所以删除时注意不要删除通过getIdentifier()引用的资源。
Analyze -> Run Inspection by Name -> 输入:Unused resources -> 跳出弹框选择范围即可
⑤ Proguard代码混淆
Proguard是一款免费的Java类文件压缩器、优化器和混淆器,Android Studio已经集成了这个工具,只要经过简单的配置,即可完成,如下代码所示,在build.gradle里面设置minifyEnabled 为ture,同时在proguardFiles指向proguard的规则文件即可。
android {
buildTypes{
minifyEnabled true
proguardFiles 'proguard.cfg'
}
}
⑥ 打开shrinkResources
shrinkResources是在编译过程中用来检测并删除无用资源文件,也就是没有引用的资源,minifyEnabled 这个是用来开启删除无用代码,比如没有引用到的代码,所以如果需要知道资源是否被引用就要配合minifyEnabled使用,只有两者都为true时才会起到真正的删除无效代码和无引用资源的目的。打开方式也是非常简单,在build.gralde文件里面打开即可:
android {
buildTypes{
minifyEnabled true
shrinkResources true
}
}
⑦ 使用AndResGuard压缩
通过将资源路径 res/drawable/wechat 变为 r/d/a 的方式来减少 apk 的大小,当 apk 有较多资源项的时候,效果比较明显,这是一款微信开源的工具,他的原理类似Java Proguard。详细使用方法参照Github,详细地址是:AndResGuard
⑧ 指定语言
如果没有特殊的需求的话,可以只编译中文,因为其他的语言用不上,如果用不上的语言编译了,会在 resource 的表里面占用大量的空间,故
android {
defaultConfig {
...
// 仅支持 中文
resConfigs "zh"
}
}
三、如何减少 so 库资源大小
① 自己编译的 so
release 包的 so 中移除调试符号。可以使用 Android NDK 中提供的 arm-eabi-strip 工具从原生库中移除不必要的调试符号。
如果是 cmake 来编译的话,可以再编辑脚本添加如下代码
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")
② 别人编译的 so
联系作者修改,一般很难联系到。
③ 动态下发 so
可以通过服务器下发 so , 下载完后再进入应用,但是体验不好,但是是一个思路。
④ 只编译指定平台的 so
一般我们都是给 arm 平台的机器开发,如果没有特殊情况,我们一般只需要考虑 arm 平台的。具体的方法是 app 下的 build.gradle 添加如下代码
android {
defaultConfig {
ndk {
abiFilter "armeabi"
}
}
}
各个平台的差别如下:
平台 | 说明 |
---|---|
armeabi-v7a | arm 第 7 代及以上的处理器,2011 年后的设备基本都是 |
arm64-v8a | arm 第 8 代 64 位处理器设备 |
armeabi | arm 第 5、6 代处理器,早期的机器都是这个平台 |
x86 | x86 32 位平台,平板和模拟器用的多 |
x86_64 | x86 64 位平台 |
四、如何减少代码资源大小
① 一个功能尽量用一个库
比如加载图片库,不要 glide 和 fresco 混用,因为功能是类似的,只是使用的方法不一样,用了多个库来做类似的事情,代码肯定就变多了。
② 混淆
混淆的话,减少了生成的 class 大小,这样积少成多,也可以从一定层度减少 apk 的大小。
③ R 文件内联
通过把 R 文件里面的资源内联到代码中,从而减少 R 文件的大小。
可以使用 shrink-r-plugin 工具来做 R 文件的内联
五、参考文档
-
APK瘦身-是时候给App进行减负了
-
Android apk包瘦身的几种方式
-
Android 性能优化总结
-
深入探索 Android 包体积优化(匠心制作-上)
-
深入探索 Android 包体积优化(匠心制作-下)