通过基准配置文件提升你的Android应用性能
应用程序性能与用户体验直接相关,具有至关重要的意义。在开发者社区中,存在许多方法来提高应用程序性能。在本文中,您将通过利用基准配置文件(Baseline Profile)深入了解其中一种技术。
基准配置文件使您能够实现更快的代码执行,从首次启动开始可能会带来约20-30%的潜在速度提升。这通过提供预编译的源代码信息来实现,有效地绕过用户启动应用程序时的解释和即时(JIT)编译步骤。
[Baseline Profiles] https://developer.android.com/topic/performance/baselineprofiles/overview
[ just-in-time (JIT)] https://source.android.com/docs/core/runtime/jit-compiler#flow
当您将基准配置文件集成到应用程序中时,Android Runtime(ART)通过利用提供的源代码配置文件来优化精确的代码路径。这些配置文件包含了关于您在Ahead-of-Time(AOT)编译中使用的类和方法的信息。
[Android Runtime (ART)] https://source.android.com/docs/core/runtime
[Ahead-of-Time(AOT)] https://source.android.com/docs/core/runtime#AOT_compilation
对于库作者来说,基准配置文件为提供最佳开发者体验提供了极好的选择。基准配置文件也可以打包在库中,并随后被引入到用户应用程序中,利用库的代码路径来提升用户应用程序的性能。
集成基准配置文件的理想场景
在大多数情况下,基准配置文件可以按照前面描述的方式提升应用程序的性能。然而,在您的项目依赖于许多第三方库,包括流行的Jetpack库的情况下,这种改进尤为明显。在这种情况下,您的应用程序会获得更大幅度的优化和性能提升。
例如,如果您的应用程序依赖于大量的Jetpack Compose库,在应用程序初始启动时可能会遇到一些轻微的卡顿或延迟。这是一个预期的结果,因为Compose是一个库,所以它不参与系统资源共享和Android平台。因此,当应用程序启动时,ART应该会对这些库进行大量解释,并在需要功能时运行JIT解释,从而降低了应用程序的性能并导致启动缓慢。
虽然某些Jetpack库已经包含了基准配置文件,但请注意大多数Jetpack库并未包含此功能。此外,许多来自GitHub或开发者社区的库也没有基准配置文件。
根据您的项目具体情况,如果您希望优化应用程序性能,建议将基准配置文件集成到项目中,而不是依赖库提供它们。
设置基准配置文件模块
现在,让我们开始配置项目的基准配置文件。从AGP 8.0或更高版本开始,您可以方便地使用基准配置文件Gradle插件。该插件简化了基准配置文件的创建过程,提供了包过滤选项,并提供更方便的功能,包括风格控制。
如果您正在使用Android Studio Iguana(或更高版本)以及Android Gradle插件8.2,您可以通过使用Android Studio提供的模板轻松创建基准配置文件模块。
当您在Android Studio中导航到以下步骤:File -> New -> New Module时,您将看到基准配置文件生成器模板,如下图所示:
确保您已经确定了要为基准配置文件生成目标的特定项目或模块,并相应地配置基准配置文件模块的包名。点击“Finish”按钮后,您将看到创建了一个包含BaselineProfileGenerator.kt文件的模块。
如果您的项目包含多个变种,并且希望控制行为,例如将所有变种生成的配置文件合并为一个统一的配置文件,或者在项目构建或发布组装期间自动化生成基准配置文件,请参阅配置基准配置文件生成指南,获取详细的说明。
用于库的基准配置文件
对于库作者来说,将基准配置文件包含在库中可以增强开发者体验。要为您的库设置基准配置文件,只需像为目标模块配置它一样添加基准配置文件插件即可。
//baselineprofile_library.kt
plugins {
id("com.android.library")
id("androidx.baselineprofile")
}
接下来,将在之前的部分中创建的基准配置文件依赖项包含进来。
//baseline_profile_module.kt
dependencies {
...
// Add a `baselineProfile` dependency on the `:baseline-profile` module.
baselineProfile(project(":baseline-profile"))
}
最后,您必须设置包过滤器,如下所示,以防止在生成的基准配置文件中包括所有不相关的类和方法信息:
//baselineprofile_filter.kt
baselineProfile {
// Filters the generated profile rules.
// This example keeps the classes in the `com.library` package all its subpackages.
filter {
include "com.mylibrary.**"
}
}
生成基准配置文件
现在,您可以直接从Android Studio的运行对话框中生成基准配置文件,就像下面的图片所示:
在大多数情况下,您可以使用BaselineProfileGenerator.kt
文件中提供的基本场景来生成基准配置文件。可以通过选择上述菜单选项或将以下命令行输入到终端中来实现:
./gradlew generateBaselineProfile
如果执行成功,您将在每个模块下的/src/main/generated/baselineProfiles
目录中发现生成的名为baseline-prof.txt
的基准配置文件。当您点击文本文件时,将会看到类和方法声明,如下所示:
正如前面的截图所示,基准配置文件包含与您的应用程序或库相关的类和方法信息。这些信息用于在Android运行环境中优化执行效率。
如果由于任何原因无法生成基准配置文件,请考虑以下故障排除步骤:
- 仔细检查您的
baselineprofiles
模块是否具有正确的targetProjectPath
,并且您的目标项目模块是否成功配置了基准配置文件插件。 - 如果您的项目配置了多个flavor或版本名称后缀,请确保在
baselineprofiles
模块和BaselineProfileGenerator.kt
文件中使用的包名适用于您项目特定的flavor或版本。
分析APK和AAB文件
在生成基准配置文件后,让我们来检查APK/AAB文件。现在,您可以验证基准配置文件是否已正确集成到您的APK/AAB文件中,并准备好交付给用户。
创建项目的APK/AAB文件后,您可以通过Android Studio上的Build -> Analyze APK菜单打开它。如果您打开APK/AAB文件,您将在assets/dexopt/baseline.prof
目录下找到混淆的基准配置文件(baseline.prof
);对于AAB来说,路径是BUNDLE-METADATA/com.android.tools.build.profiles/baseline.prof
。
在将项目打包成APK/AAR结果时,baseline.prof
文件从您提供的baseline-prof.txt
中生成,并将其混淆为dex文件。但是这个baseline.prof
文件如何增强您的应用程序性能呢?关键在于DEX优化器,也称为Dexopt。
https://source.android.com/docs/core/runtime/configure
如果您的APK/AAR文件包含baseline.prof
文件,Dexopt将按照以下三个主要过程来改善应用程序性能:
- Dex到OAT编译:首先,.dex文件被转换为一种更高效的格式,称为OAT(Optimized Android Application Package)。
- 预编译(AOT)编译:在此步骤中,OAT文件通过预先编译进一步进行优化。该过程包括为安装应用程序的设备的特定CPU架构优化代码。它减少了启动应用程序所需的时间,并可以提高整体运行时性能。
- 缓存:优化的OAT文件被缓存,因此它们可以在应用程序的后续启动中快速访问,减少了重复优化的需求。
因此,DEX优化器利用您的基准配置文件来提升应用程序的启动时间和运行时性能,按照上述步骤进行操作。
分析 AAR 文件
如果您是一个库的作者,您可以验证您的 AAR 文件是否准确地包含了基准配置文件。编译您的项目,并使用 Android Studio 打开您的 AAR 文件,步骤与前面的部分相同,然后您将看到下面的结果:
如上面的截图所示,您会注意到 baseline-prof.txt 文件已经安全地嵌入在您的 AAR 文件中。此时,您可能会想知道,“为什么它带有 .txt 扩展名,而不像 APK/AAB 文件一样呢?”
这个问题的答案可以在IssueTracker: Baseline Profile is not obfuscated for AAR library(问题跟踪器: AAR 库的基准配置文件没有被混淆)中找到。根据基准配置文件团队的回复,“库通常不会被混淆,而且 AGP (Android Gradle 插件)目前没有区分库的调试版和发布版。”因此,如果您注意到一个 .txt 文件而不是一个 .prof 文件,那完全是符合预期的。
1.5 MB 的限制
官方的 Android 文档指出,“编译的基准配置文件必须小于 1.5MB。”让我们来探讨一下这个限制背后的原因。
导航到生成的 baseline-prof.txt
文件存储的目录,并检查文件的属性。您可能会注意到文件大小超过了 1.5 MB 的限制,通常在 4 到 10 MB 或更高的范围内。
那么我们不能将基准配置文件包含在 APK/AAB 文件中吗?答案是否定的。官方的 Android 文档中说,“编译的二进制基准配置文件”,而不是文本文件。那么让我们看一下编译的二进制基准配置文件的大小。
为了调查这个问题,使用 Android Studio 的 Analyze APK 菜单打开您的 APK/AAB 文件,并访问 assets/dexopt/
目录。在里面,您会找到 baseline.prof
文件。检查其属性后,您会注意到文件大小通常在 10 到 30 KB 或更高的范围内。
确实,这个大小明显小于 1.5 MB,对吗?当您将基准配置文件打包到 APK/AAB 文件中时,大部分的配置文件信息会进行优化,从而大大减小了编译后的文件大小。在大多数情况下,您不需要过度担心指定的限制。
提升基准测试场景
现在您已经深入了解如何生成基准配置文件并理解其在应用程序中的集成,还有一个关键方面可以为您的启动和运行时性能注入活力——创建有效的基准测试场景。
如果您依赖于 BaselineProfileGenerator.kt
文件中提供的默认场景,那么需要注意的是,该场景只捕获您应用程序的初始启动器活动,没有考虑其他用户体验和行为。因此,您的基准配置文件可能缺乏所需的全面信息,以涵盖用户可能遇到的更广泛的场景。
为了调整您的基准配置文件以涵盖更广泛的用户交互,应使用 Macrobenchmark
和 UIAutomator
库创建自己的应用程序场景。您可以创建自动化的 UI 测试,从而模拟各种用户操作,包括导航到不同的屏幕、点击按钮、滚动、与 UI 组件进行交互,甚至模拟物理设备按钮按下。
基于您的自动化 UI 测试,Macrobenchmark
扩展其分析和记录,生成具有更广泛应用范围的基准配置文件,捕获更广泛的配置文件信息。如果您有兴趣学习如何创建高效的基准测试场景,请参阅以下 GitHub 存储库:
Stream Video Android: Dogfooding Scenarios
https://github.com/GetStream/stream-video-android/blob/develop/benchmark/src/main/kotlin/io/getstream/video/android/benchmark/DogfoodingScenarios.kt
Now in Android: ForYouActions
https://github.com/android/nowinandroid/blob/main/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/foryou/ForYouActions.kt
Tivi: App Scenarios
https://github.com/chrisbanes/tivi/blob/main/android-app/common-test/src/main/kotlin/app/tivi/app/test/AppScenarios.kt#L17
测量启动性能
现在,让我们来评估基准配置文件对应用程序启动和运行时性能的真实影响。您可以使用 Macrobenchmark 创建基准测试代码来轻松度量应用程序的启动性能,如下面的示例所示:
//startup_benchmarks.kt
class StartupBenchmarks {
@get:Rule
val rule = MacrobenchmarkRule()
@Test
fun startupCompilationNone() =
benchmark(CompilationMode.None())
@Test
fun startupCompilationBaselineProfiles() =
benchmark(CompilationMode.Partial(BaselineProfileMode.Require))
private fun benchmark(compilationMode: CompilationMode) {
rule.measureRepeated(
packageName = "YOUR_APPLICATION_PACKAGE_NAME",
metrics = listOf(StartupTimingMetric()),
compilationMode = compilationMode,
startupMode = StartupMode.COLD,
iterations = 10,
setupBlock = {
pressHome()
},
measureBlock = {
startActivityAndWait()
device.waitForIdle()
// app sceniarios
},
)
}
}
确保您使用了正确的包名,并且您的应用程序场景与 BaselineProfileGenerator.kt
文件中使用的场景相匹配。在提供的代码片段中,您可以观察到您正在为应用程序的性能进行两个场景的基准测试:一个是没有基准配置文件的情况,另一个是有基准配置文件的情况。
执行性能测量测试后,您将在Android Studio中看到如下所示的报告:
StartupBenchmarks_startupCompilationBaselineProfiles
timeToInitialDisplayMs min 341.7, median 419.6, max 765.2
Traces: Iteration 0 1 2 3 4 5 6 7 8 9
StartupBenchmarks_startupCompilationNone
timeToInitialDisplayMs min 371.4, median 434.7, max 815.9
Traces: Iteration 0 1 2 3 4 5 6 7 8 9
报告显示,基准配置文件对应用程序性能的提升有助于在最小/最大情况下提升7%到10%的性能。
如果测量结果表明非基准配置文件情况下的性能更好,那么就需要仔细审查您的应用程序场景是否严重依赖于各种功能,并且根据不同情况进行选择,例如大量的网络或数据库请求。
使用 GitHub Actions 生成基准配置文件
通常情况下,您可以通过使用以下插件选项在创建发布版本时生成基准配置文件:
//baselineprofile_auto_generation.kt
baselineProfile {
variants {
freeRelease {
automaticGenerationDuringBuild = true
}
}
}
您还可以通过 GitHub Actions 根据自己的偏好生成针对更具体场景的基准配置文件,如下面的脚本所示:
# baseline_github_actions.yml
name: baseline-profiles
run-name: ${{ github.actor }} requested a workflow
# This should be a manual trigger so this actions gets executed every time make a new pull request.
# Change this event to what suits your project best.
# Read more at https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows
on:
workflow_dispatch:
jobs:
generate-baseline-profiles:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 17
- name: Setup Gradle
uses: gradle/gradle-build-action@v2.9.0
- name: Grant Permissions to gradlew
run: chmod +x gradlew
- name: Build app and benchmark
run: ./gradlew :app:assembleBenchmark # change the 'app' with your app module's name
- name: Clean Managed Devices
run: ./gradlew cleanManagedDevices --unused-only
# Generates Baseline Profile
- name: Generate Baseline Profile
run: ./gradlew generateBaselineProfile -Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect" -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile -Pandroid.experimental.testOptions.managedDevices.setupTimeoutMinutes=20 -Dorg.gradle.workers.max=4
# Create Pull Request
- name: Create Pull Request
uses: peter-evans/create-pull-request@v5
with:
commit-message: "Generate baseline profiles"
title: "Generate baseline profiles"
delete-branch: true
branch: actions/baseline-profiles
当工作流运行时,它将自动发起一个拉取请求,并更新基准配置文件,如下图所示:
结论
在本文中,您深入了解了基准配置文件的全面理解。您探究了基准配置文件如何在 Dex 优化器中运行,学习了如何分析您的 APK/AAB/AAR 文件,发现了如何创建基准测试场景,以及如何测量应用程序的性能。
如果您想了解更多关于基准配置文件和基准测试的信息,请查看 基准配置文件概述 和 Jetpack Benchmark 1.2.0的新功能。
https://developer.android.com/topic/performance/baselineprofiles/overview