Android NDK开发详解之调试和性能分析的DEX 布局优化和启动配置文件
实验性:使用启动配置文件进行 DEX 布局优化是一项需要添加实验性标记的功能。
本文档介绍了在构建 Android 应用时可以通过哪些步骤启用使用启动配置文件进行 DEX 布局优化的功能。
重要提示:本文档假定您熟悉基准配置文件的相关工作流程。为了简化设置,请使用基准配置文件 Gradle 插件。
要求
如果想使用 DEX 布局优化及相关功能,您需要安装 Jetpack Macrobenchmarks 版本 1.2.0-alpha14 或更高版本,以及下表中所列的 Android Gradle 插件版本:
目前不支持 Bazel 等第三方构建系统。
启动配置文件
启动配置文件与基准配置文件类似。此类配置文件描述了对应用启动至关重要的类和方法,这些类和方法必须先做好加载准备。启动配置文件也使用与基准配置文件相同的人类可读格式 (HRF)。
注意:应用启动通常是多个关键 CUJ 的组合。对于简单的应用,您可以启动应用的 MainActivity。不过,您可能需要组合关键 CUJ,例如登录和其他重要的启动功能。
启动配置文件和基准配置文件之间的一个主要区别是,基准配置文件包含对应用启动以外的优化至关重要的类和方法,例如减少动画期间或应用启动以外的关键用户历程 (CUJ) 期间的卡顿。
启动配置文件和基准配置文件之间的另一个主要区别是,启动配置文件无法由库提供,也不受 Android Gradle 插件合并的约束。这是因为库不一定充分了解应用启动的关键类和方法有哪些。这些类和方法最好衍生自 Jetpack Macrobenchmark,并将 BaselineProfileRule 与专门针对应用启动的 CUJ(例如 collectStableBaselineProfile)结合使用。
请参阅下表,快速了解基准配置文件与启动配置文件之间的主要区别。
启动配置文件存储在 src//main/baselineProfiles/startup-prof.txt 中。
DEX 布局优化
这项优化能够改进启动期间所用代码的位置,缩短启动时间,从而减少应用启动期间发生的主要页面故障的数量。
为此,需要将启动期间要执行的所有代码添加到主要 classes.dex 文件中,同时将所有非启动代码从主要 classes.dex 文件中移除。
图 1. DEX 布局优化的代码位置改进。
使用 DEX 布局优化
本部分介绍了如何使用 DEX 布局优化。
构建启动配置文件
首先,您需要构建启动配置文件。
项目设置
在前面的示例中,插桩测试驱动了一个软件包名称为 com.example.app 的 app 模块。
当您使用 Jetpack Macrobenchmark 生成启动配置文件规则时,您会在 build 中定义新的基准变体。基准变体与发布变体相同,不过您会关闭缩减功能。
以下代码摘自应用模块的 build.gradle 文件。
Kotlin
buildTypes {
...
create("benchmark") {
initWith(buildTypes.getByName("release")
isMinifyEnabled = false
signingConfig = signingConfigs.getByName("debug")
matchingFallbacks += "release"
}
}
Groovy
buildTypes {
...
create("benchmark") {
initWith(buildTypes.getByName("release")
isMinifyEnabled = false
signingConfig = signingConfigs.getByName("debug")
matchingFallbacks += "release"
}
}
生成启动规则
Kotlin
@RunWith(AndroidJUnit4::class)
class StartupProfileGenerator {
@get:Rule
val baselineProfileRule = BaselineProfileRule()
@Test
fun startup() =
baselineProfileRule.collectStableBaselineProfile(
packageName = "com.example.app",
includeInStartupProfile = true
) {
// This scenario just starts the activity and waits for it to draw
// the first frame. If you have animations or async content in your
// startup, wait for them with UiAutomator.
startActivityAndWait()
}
}
Java
@RunWith(AndroidJUnit4::class)
class StartupProfileGenerator {
@get:Rule
val baselineProfileRule = BaselineProfileRule()
@Test
fun startup() =
baselineProfileRule.collectStableBaselineProfile(
packageName = "com.example.app",
includeInStartupProfile = true
) {
// This scenario just starts the activity and waits for it to draw
// the first frame. If you have animations or async content in your
// startup, wait for them with UiAutomator.
startActivityAndWait()
}
}
注意:虽然启动配置文件和基准配置文件共享 HRF 格式,但启动配置文件不参与合并,并且是 build 的显式输入。
输出
运行测试将会生成如下所示的文件:
HSPLandroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
HSPLandroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
HLandroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
PLandroidx/compose/runtime/CompositionImpl;->applyChanges()V
HLandroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I
Landroidx/compose/runtime/ComposerImpl;
注意:进行启动 DEX 布局优化时,系统只会考虑启动 CUJ 生成的规则。这意味着系统将忽略通过合并库规则衍生的所有规则。
将输出文件的内容复制到 src/main/baselineProfiles/startup-prof.txt。
启用 DEX 布局优化
如需启用 DEX 布局优化,请使用以下 Gradle 代码段:
Kotlin
android {
// ...
experimentalProperties["android.experimental.art-profile-r8-rewriting"] = true
experimentalProperties["android.experimental.r8.dex-startup-optimization"] = true
}
Groovy
android {
// ...
experimentalProperties["android.experimental.art-profile-r8-rewriting"] = true
experimentalProperties["android.experimental.r8.dex-startup-optimization"] = true
}
android.experimental.art-profile-r8-rewriting
实验性属性 android.experimental.art-profile-r8-rewriting 支持通过 D8 和 R8 重写基准规则。
必须执行此步骤,因为 D8 和 R8 会执行类合并、合成、重写 lambda 以及方法签名优化等优化。
这些优化可以更改现有类的方法签名,并创建从现有源代码符号派生的新类。执行这些转换时,D8 和 R8 会对人类可读的基准和启动配置文件规则执行同一组转换。
此步骤对于全面捕获优化应用性能所需的所有规则非常重要。启用 R8 规则重写可将配置文件质量提高多达 25%。
注意:您还可以使用 experimental.art-profile-r8-rewriting 提高基准配置文件的配置文件质量。
android.experimental.r8.dex-startup-optimization
实验性属性 android.experimental.r8.dex-startup-optimization 支持 DEX 布局优化。
注意:启用此标记后,您会看到主要 DEX 文件将缩减为仅包含应用启动所需的类。如果应用之前从未使用过 MultiDex,则现在需要启用 MultiDex。
如需构建具有 DEX 布局优化功能的 APK,您可以使用以下代码:
./gradlew :app:assembleRelease