一、包体积优化的必要性
装置包大小与下载转化率的相关大抵是成正比的,即装置包越大,下载转换率就越差。Google 曾在2019的谷歌大会上给出过一个统计论断,包体积体大小每回升6MB,运行下载转化率就会降低1%,在不同地域的体现或许会有所差异。
APK 增加10MB,在不同国度转化率增长
二、游戏中心 APK 组成
APK 蕴含以下目录:
发现占包体积比拟大的重要是 lib、res、assets、resources 这几个局部,优化重要也从这几个方面入手。
三、包体积检测工具
Matrix-ApkChecker 作为 Matrix 系统的一局部,是针对Android 装置包的剖析检测工具,依据一系列设定好的规定检测 APK 能否存在特定的疑问,并输入较为详细的检测结果报告,用于剖析排查疑问以及版本追踪。
性能游戏中心的 Json,重要检测 APK 能否经过了资源混杂、不含 Alpha 通道的 PNG 文件、未经紧缩的文件类型、冗余的文件、无用资源等消息。
关于生成的检测文件启动剖析,可以优化不少体积。
工具 Matrix Apkcheck 引见:
四、包体积优化措施
名目中存在较多这种类型的图,可以交流为 JPG 或许 WebP 图,能增加不少体积。
随着业务的迭代,很多业务场景是不会再经常使用了,触及到相关的资源和类文件都可以删除掉,相应的 APK 中 res 和 dex 都会相应增加。游戏中心这次去掉了些经过迭代后没有经常使用的业务场景和资源。
针对外销的名目,本地的 string.xml 或许 SDK 中的 string.xml 文件中的多言语,是基本用不到的。这局部资源可以优化掉,能增加不少体积。
在APP的 build.gradle 中下参与 resConfigs "zh-rCN", "zh-rTW", "zh-rHK"。这样性能不影响英文、中文、中国台湾繁体、中国香港繁体言语的展现。
资源文件起码化性能前
资源文件起码化性能后
很多名目为了适配各种尺寸的分辨率,同一份资源或许在不同的分辨率的目录下搁置了各种文件,而后如今干流的机型都是 xxh 分辨率,游戏游戏中心针对了内置的 APK,性能了优先经常使用"xxhdpi", "night-xxhdpi"。
这么性能假设 xxhdpi、night-xxhdpi 存在资源文件,就会优先经常使用该分辨率目录下文件,假设不存在则会取原来分辨率目录下子资源,能防止产生资源找不到的情景。
defaultConfig {resConfigs isNotBaselineApk ? "" : ["xxhdpi", "night-xxhdpi"]}
左右滑动检查完整代码
雷同关于内置包来说,必需都是 Android 7 及以上的机型了,可以思索去掉v1签名。
signingConfigs {gameConfig {if (isNotBaselineApk) {print("v1SigningEnabled true")v1SigningEnabled true} else {print("v1SigningEnabled false")v1SigningEnabled false}v2SigningEnabled true}}
去掉v1签名后,上图的三个文件在 APK 中会隐没,也能较少 600k 左右的体积。
4.6动效资源文件优化
发现名目中用了不少的 GIF、Lottie 文件、SVG 文件,占用了很大一局部体积。思索将这局部交流成更小的动画文件,目前游戏中心接入了 PAG 打算。交流了局部 GIF 图和 Lottie 文件。
PAG 文件驳回可扩展的二进制文件格局,可单文件集成图片音频等资源,导出相反的 AE 动效内容,在文件解码速度和紧缩率上均大幅上游于同类型打算,大约为 Lottie 的0.5倍,SVG 的0.2倍。
实践上或许因为设计导出的 Lottie 或许 GIF 不规范,在导出 PAG 文件时会提示优化点,实践局部资源的紧缩比率到达了80~90%,局部动效资源从几百K降到了几十K。
详细可以参考 PAG 官方:
游戏中心这边将比拟大的 GIF 图,较多的 Lottie 图做过 PAG 交流。
举例:
(1)游戏中心的榜单排行页上的头图,UI那边导出的合乎成果的 GIF 大小为701K,交流为 PAG 格局后雷同成果的图大小为67K,只要原来的1/10不到。
(2)游戏中心的入口空间 Lottie 动效优化。
一份 Lottie 动效大略是这样的,一堆资源疑问加上 Json 文件。像上述动效的全体资源为112K,雷同的动效格局转换为 PAG 格局后,资源大小变成6K,只要原大小的5%左右。之后新的动效会优先思索经常使用 PAG。
4.7 编译时期优化图片
以游戏中心 App 为例,图片资源约占用了25%的包体积,因此对图片紧缩是能立杆奏效的形式。
WebP 格局相比传统的 PNG 、JPG 等图片紧缩率更高,并且同时允许有损、无损、和透明度。
思绪就是在是在 mergeRes 和 processRes 义务之间拔出 WebP 紧缩义务,应用 Cwebp 对图片在编译时期紧缩。
已有的处置方法:
(1)可以驳回滴滴的打算 booster,booster-task-compression-cwebp 。
参考链接:
(2)公司外部官方模块也有相似基于 booster 的插件,基于 booster 提供的 API 成功的图片紧缩插件。紧缩事先须要对一切页面启动一次性点检,防止图片失真,针对失真的图片,可以驳回白名单的机制。
4.8灵活化加载so
雷同以游戏中心为例,so的占比到达了45.1%,可以对经常使用场景较少和较大的so进执行态化加载的战略,在须要经常使用的场景下载到本地,灵活去加载。
经常使用的场景去服务端下载到本地加载的流程可以由以下流程图示意。
流程可以演绎为下载、解压、加载,重要疑问就是处置so加载疑问。
载入so库的传统做法是经常使用:
System.loadLibrary(library);
经常会产生 UnsatisfiedLinkError,Relinker 库能大幅减小报错的概率:
ReLinker.loadLibrary(context, "mylibrary")
详细可以参考:
按需加载的情景,危险与收益是并存的,有很多状况须要思索到,比如下载触发场景、网络环境、加载失败能否有升级战略等等,也须要做好给用户的提示交互。
4.9内置包只放64位so
目前新上市的手机 CPU 架构都是arm64-v8a, 对应着 ARMV8 架构,所以在打包的时刻针对内置名目,只打包64位so出来。
ndk {if ("64" == localMultilib)abiFilters "arm64-v8a"else if ("32" == localMultilib)abiFilters "armeabi"elseabiFilters "armeabi", "arm64-v8a"}//其中localMultilib为性能项变量String localMultilib = getLocalMultilib()String getLocalMultilib() {def propertyKey = "LOCAL_MULTILIB"def propertyValue = rootProject.hasProperty(propertyKey) ? rootProject.getProperty(propertyKey) : "both"println " --> ${project.name}: $propertyKey[$propertyValue], $propertyKey[${propertyValue.class}]"return propertyValue}
左右滑动检查完整代码
4.10开启代码混杂、移除无用资源、ProGuard 混杂代码
android {buildTypes {release {minifyEnabled trueshrinkResources true}}}
shrinkResources 和 minifyEnabled 必需同时开启才有效。
特意留意:这里须要强调一点的是开启之后无用的资源或许图片并没有真正的移除掉,而是用了一个同名的占位符号。
可以经过 ProGuard 来成功的,ProGuard 会检测和移除代码中未经常使用的类、字段、方法和属性,除此外还可以优化字节码,移除未经常使用的代码指令,以及用短称号混杂类、字段和方法。
proguard-android.txt 是 Android 提供的自动混杂性能文件,在性能的 Android sdk /tools/proguard 目录下,proguard-rules.pro是咱们自定义的混杂性能文件,咱们可以将咱们自定义的混杂规定放在外面。
android {buildTypes {release {minifyEnabled trueproguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'}}}
左右滑动检查完整代码
4.11R文件内联优化
假设咱们的 App 架构如下:
编译打包时每个模块生成的 R 文件如下:
R_lib1 = R_lib1;R_lib2 = R_lib2;R_lib3 = R_lib3;R_biz1 = R_lib1 + R_lib2 + R_lib3 + R_biz1(biz1自身的R)R_biz2 = R_lib2 + R_lib3 + R_biz2(biz2自身的R)R_app = R_lib1 + R_lib2 + R_lib3 + R_biz1 + R_biz2 + R_app(app自身R)
左右滑动检查完整代码
可以看出各个模块的R文件都会蕴含高层组件的R文件内容,高层的模块生成的id除了自己会生成一个R文件外,同时也会在全局的R文件生成一个,R文件的数量雷同会收缩回升。多模块状况下,会造成 APK 中的 R 文件将急剧的收缩,对包体积的影响很大。
因为App模块目前的R文件中的资源ID所有是 final 的, Java 编译器在编译时会将 final 常量启动 inline 内联操作,将变量交流为常量值,这样名目中就不存在关于 App 模块R文件的援用了,这样在代码缩减阶段,App 模块R文件就会被移除,从而到达包体积优化的目标。
基于以上原理,假设咱们将 library 模块中的资源 ID 也转化为常量的话,那么 library 模块的R文件也可以移除了,这样就可以有效地增加咱们的包体积。
如今有不少开源的R文件内联方法,比如滴滴开源的 booster 与字节开源的 bytex 都蕴含了R文件内联的插件。
booster 参考:
bytex 参考:
五、优化成果
5.1 优化成果
上述优化措施均在游戏中心实践中驳回,以游戏中心某个相反的版本为例子,前后体积对比如下图所示:
(1)包体积优化的比例到达了31%,包体积降低了20M左右,从短暂来说对运行的转换率可以优化3%的点左右。
(2)启动速度相关于未优化版本优化2.2%个点。
5.2 总结
(1)读者想启动体积优化之前,需先剖析下 APK 的各个模块占比,重要针对占比高的局部启动优化,比如:游戏中心中 lib、res、assets、resources 占比拟高,就针对性的启动了优化;
(2)动效打算的切换、so灵活加载、编译时期图片优化等措施是短暂的,相比于未启动优化,时期越长或许增加的体积越清楚;
(3)资源文件最小化性能、性能资源优化,便捷且成果清楚;
(4)后续会对 dex 进后退一步探求,目前名目中代码基本上都在做加法,越来越复杂,很少有做减法,造成 dex 逐渐增大,目前还在探求怎样进一步增加 dex 体积。