Android 性能优化技术月报 | 2024 年 7 月
每个月都会有一些 Android 性能优化相关的优质内容发布,然而,碎片化阅读使得这些知识难以形成完整体系,且容易被遗忘。为解决这些问题,我决定尝试使用技术月报的形式,总结我在最近一个月内查阅的 Android 性能优化相关的优质内容。
月报的主要内容包括:整理展示我在最近一个月所查阅的 Android 性能优化领域的最新技术动态、精选博客,精选视频等内容。
精选博客
哔哩哔哩APP的 AGP8 升级之旅
本文主要讲述了哔哩哔哩 APP 的 AGP8 升级之旅。涵盖了从 Android 大仓的 AGP7.2.2 升级到 AGP8.2.2 的全过程,具体包括以下内容:
- Transform Api:检测并修改项目中使用的 Transform Api 插件,自研插件参照官网文档修改,第三方华为推送插件参考官网文档。
- Namespace:默认开启,可使用脚本去除 xml 中的 package,并在 gradle 中添加。
- BuildConfig:默认不生成,全局默认关闭,部分模块按需开启。
- nonFinalResIds:默认,无法在某个模块单独开启,只能全局开启,特殊情况在 ci 脚本中单独关闭。
- JVM 相关:AGP 升级后,JDK 需使用 17 版本,Gradle 默认 target 有修改,可全局修改提前上线。
- Gradle 升级:从 7 升级到 8,部分官方插件有对应修改,不同设备可能有编译报错,需安装相关工具链。
- 第三方库:大部分第三方库适配 AGP8,个别库如 greendao 需特殊适配,okio 版本自动依赖导致编译报错,通过手动压制和添加 lint 检查解决。
- Transform:新 Transform 的 output 是 jar 文件,实际使用坑多,需实现增量编译,降低 jar 压缩率、使用 buffer 操作、多线程等可优化编译时间。
- nonTransitiveRClass:项目因历史原因无法开启,自行写插件收集模块资源,根据 PSI 接口替换 R 的导入。
- R8 问题:部分 C++ 调用 Java 代码时,使用 R8 编译会出现问题,需添加参数禁用优化,mapping.txt 文件格式变化,APK 中无法默认剔除 META-INF/**_release.kotlin_module,需手动处理。
- 数据劣化与治理:升级过程中编译耗时和产物体积有劣化,通过一系列优化措施部分改善,开启 nonTransitiveRClass 特性对模块变异速度和单个 Jar 文件大小有优化。
云音乐 Android Baseline Profiles 实践
Baseline Profiles 的整体工作流程可分为三个部分:
- 生成人类可读格式(HRF)的配置文件
- 构建 APK 时,将 HRF 配置文件转换为二进制格式并写入 APK 中的 assets/dexopt 目录
- 应用市场安装时,用 APK 中的配置文件进行 AOT 编译;或启动时,用 ProfileInstaller 将配置文件写入系统规定路径
本文先介绍了 Baseline Profiles 的工作方式,在此基础上探讨了面临的三个问题的解决方案:
- 国内只有部分应用市场支持 Baseline Profiles:追加主动触发优化来尽可能加快优化进程
- 加固插入 dex 导致 crc 校验失败,不执行编译:修正加固后 APK 种配置文件记录的 dex 名
- 对热修复后运行的 dex 无效:在补丁合成阶段触发 dex 优化前,将配置信息写入合成后 dex 对应的配置文件
云音乐在解决了上述问题,使 Baseline Profiles 按预期方式工作后,启动性能得到了明显提升:
- 应用市场支持时提升了约 31%;整体提升了约 6%,且会随着支持的应用市场的增加而进一步提升
- 热修复后提升了约 12%
为什么Android平台的虚拟机是基于寄存器设计的?
为什么 JVM 是基于栈设计的,而 Android 平台的虚拟机是基于寄存器设计的?
很显然,既然它们同时存在,那就意味着它们各有优劣,假设其中一种明显优于另外一种,那劣势的那一种便就不会存在了。如果我们对这两种架构进行对比,我们会发现它们存在如下的区别:
- 基于栈的架构具有更好的可移植性,因为其实现不依赖于物理寄存器
- 基于栈的架构通常指令更短,因为其操作不需要指定操作数和结果的地址
- 基于寄存器的架构通常运行速度更快,因为有寄存器的支撑
- 基于寄存器的架构通常需要较少的指令来完成同样的运算,因为不需要进行压栈和出栈
Android 15 16kb页对齐
Android 15已经迎来 beta 了,其中有一个更新让开发者们必须要注意,就是 google 决定在 Android15 上可以配置 16kb 的 pagesize。
从官方对于 NDK 对 16K Page Size 提供的描述看,Android V 其实还是允许 OEM 厂商自己选择 16K 还是 4K ,而其实按照 Page Size 逻辑,16K 的 so 本来就兼容 4K 的设备,因为它 16 本身就是 4 的整数倍,所以我们也不需要同时构建 16K 和 4K 版本的库,只需要 16K 就够了。
但是 Google Play 计划明年就要求 16K Page Size,出海的 App 需要尽快适配。
在适配过程中,我们需要做的主要就是:
- 确保代码里没有 mmap 、sysconf 等写死 4096(0x1000) 地方的代码
- 确保不是假对齐,例如低版本 NDK 编译下的 0x10000(65536) 64k 对齐不一定可信
- 4K 可运行的情况下, 16K 在确定 so 都是地址对齐的情况下,高度怀疑 NDK 问题
- 升级 NDK or 工具链合集,首选升级为 Clang/LLVM 相关