新环境
- 当前所使用的
Android Studio的版本为Android Studio Panda 4 | 2025.3.4 Patch 1 Gradle的版本为8.11.1AGP的版本为8.9.2
新建模块
在项目中创建一个Baseline Profile Generator模块,创建窗口中有多个参数,只说一下Target application,和Use Gradle Managed Device
其中,Target application是要生成的基准配置文件所对应的应用模块,Use Gradle Managed Device选中后,将模块设置为在自动管理的Android模拟器上运行基准配置文件生成器,使用真机的情况下,不需要勾选这个选项。完成后点击Finish
项目变化
完事之后,除了添加一个新模块之外,项目中还会发生如下的变化
gradle目录下的libs.versions.toml配置文件- 项目根目录下的
build.gradle - 项目根目录下的
settings.gradle - 项目根目录下的
gradle.properties app模块的build.gradle
libs.versions.toml
会增加如下配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| [versions] uiautomator = "2.3.0" benchmarkMacroJunit4 = "1.4.1" baselineprofile = "1.4.1" profileinstaller = "1.4.1"
[libraries] androidx-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" } androidx-benchmark-macro-junit4 = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "benchmarkMacroJunit4" } androidx-profileinstaller = { group = "androidx.profileinstaller", name = "profileinstaller", version.ref = "profileinstaller" }
[plugins] android-test = { id = "com.android.test", version.ref = "agp" } baselineprofile = { id = "androidx.baselineprofile", version.ref = "baselineprofile" }
|
如果引入上面的配置,实际上相当于
1 2 3 4 5 6 7 8 9 10
| implementation 'androidx.test.uiautomator:uiautomator:2.3.0' implementation 'androidx.benchmark:benchmark-macro-junit4:1.4.1' implementation 'androidx.profileinstaller:profileinstaller:1.4.1'
plugins { id 'com.android.test' version '8.9.2' apply false id 'androidx.baselineprofile' version '1.4.1' apply false }
|
project:build.gradle
在项目根目录下的build.gradle文件中,会增加如下配置,通过apply false只声明插件版本而不实际应用它们,待需要时在app模块的build.gradle文件中再声明,后续声明时无需再注明版本号,在当前的gradle和AGP版本中,通过alias声明插件版本,所以不明显
1 2 3 4
| plugins { alias(libs.plugins.android.test) apply false alias(libs.plugins.baselineprofile) apply false }
|
settings.gradle
没什么好说的,增加了一个模块的引用
1
| include ':baselineprofile'
|
gradle.properties
在gradle.properties文件中,会增加如下配置,启用Gradle的配置缓存功能,以加快构建速度
1
| org.gradle.configuration-cache=true
|
app:build.gradle
app模块的build.gradle文件中会增加如下配置,其中:
baselineProfile project(':baselineprofile')依赖项,让Gradle知道应该从哪个模块获取已生成的基准配置文件profileinstaller依赖项,应用首次启动时,它会读取并安装打包在APK中的基准配置文件,告诉Android Runtime(ART)哪些代码路径需要提前编译(AOT)
1 2 3 4 5 6 7 8 9 10
| plugins { alias(libs.plugins.baselineprofile) }
dependencies { implementation libs.androidx.profileinstaller baselineProfile project(':baselineprofile') }
|
Baseline Profile模块配置
Baseline Profile Generator模块的build.gradle文件中,简单分析一下如下配置
alias(libs.plugins.android.test)声明了这是一个测试模块
1 2 3 4 5
| plugins { alias(libs.plugins.android.test) alias(libs.plugins.kotlin.android) alias(libs.plugins.baselineprofile) }
|
targetProjectPath指定了要生成基准配置文件的目标应用模块,animationsDisabled则是自动关闭设备动画,保证测试结果稳定
1 2 3 4 5 6 7 8
| android { targetProjectPath = ":app"
testOptions { animationsDisabled = true } }
|
在baselineProfile配置项中,启用useConnectedDevices选项,让生成器在连接的设备上运行测试,以收集基准配置文件数据
1 2 3
| baselineProfile { useConnectedDevices = true }
|
如下依赖项,其中:
benchmark-macro-junit4是基准测试框架espresso-core和uiautomator是用于编写用户界面测试的库junit是用于编写单元测试的库
1 2 3 4 5 6
| dependencies { implementation libs.androidx.benchmark.macro.junit4 implementation libs.androidx.espresso.core implementation libs.androidx.junit implementation libs.androidx.uiautomator }
|
其他配置
实际上,在有了上面的配置之后,项目就已经具备了生成基准配置文件的能力了,但是在生成之前,我们还需要对baselineprofile模块进行一些额外的配置
在app模块的build.gradle文件中,增加如下配置:
dexLayoutOptimization让启动热点代码尽可能位于DEX前部,减少启动时的随机I/O和页面加载时间,在较新的gradle版本中,这个配置项已经被默认启用。automaticGenerationDuringBuild设置为false,避免在构建APK时自动采集Profile
1 2 3 4 5 6
| baselineProfile { dexLayoutOptimization = true automaticGenerationDuringBuild = false }
|
编写关键路径采集
BaselineProfileRule负责采集性能数据和Profile,并将其转换为基准配置文件格式,UIAutomator负责点击、滑动、长按、输入等用户界面交互操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| @get:Rule val rule = BaselineProfileRule()
@Test fun generate() { rule.collect( packageName = TARGET_PACKAGE, maxIterations = 15, stableIterations = 3, outputFilePrefix = "jqTools", includeInStartupProfile = true, strictStability = false, filterPredicate = { it !in listOf( "android.app.ActivityThread\$AppBindData", "android.app.ActivityThread\$ProviderKey", ) } ) { pressHome() startActivityAndWait()
device.waitForIdle() } }
|
查找text为 分类 的view并点击
1 2 3
| device.findObject( By.text("分类") )?.click()
|
通过资源id匹配view并点击
1 2 3 4 5 6 7
| device.findObject( By.res(TARGET_PACKAGE, "btn_login") )?.click()
device.findObject( By.res("${TARGET_PACKAGE}:id/btn_login") )?.click()
|
RecyclerView滚动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| val list = device.findObject( By.res( TARGET_PACKAGE, "rv_list" ) )
val rect = list.visibleBounds
device.swipe( rect.centerX(), rect.bottom - 100, rect.centerX(), rect.top + 100, 3 )
|
还有很多,用到的时候再查文档吧
生成基准配置文件
在旧的AGP版本中,执行如下命令(其中:baselineprofile是模块名),会在连接的设备上运行测试用例,收集性能数据,并生成基准配置文件
1
| ./gradlew :baselineprofile:generateBaselineProfile
|
但是AGP 8.2+以后,这个Task在app模块上了,所以执行如下命令
1
| ./gradlew :app:generateBaselineProfile
|
也有可能Task名字变了,执行如下命令查看一下指定的模块中有没有相应的Task
1
| ./gradlew :baselineprofile:tasks --all
|
编写性能测试
在生成基准配置文件后,可以通过运行性能测试来验证其效果,在目标设备上自动运行app并监控其性能指标,如启动时间、内存使用情况
MacrobenchmarkRule负责控制测试流程、统计性能指标、处理测试结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| @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 = TARGET_PACKAGE, metrics = listOf(StartupTimingMetric()), compilationMode = compilationMode, startupMode = StartupMode.COLD, iterations = 10, setupBlock = { pressHome() }, measureBlock = { startActivityAndWait() } ) }
|
执行性能测试时,注意将项目variants变体设置为benchmarkRelease,以确保测试的是优化后的版本
打包
生成基准配置文件之后,如果配置文件路径没在main目录下,需要将生成的基准配置文件复制到app模块的src/main/baseline-prof.txt路径下,之后可以通过如下命令将其打包到APK中
1
| ./gradlew :app:assembleRelease
|
旧环境
- 使用的
Android Studio的版本为Android Studio Panda 4 | 2025.3.4 Patch 1 Gradle的版本为8.0AGP的版本为8.0.2
新建模块
同当下新环境的模块创建
项目变化
完事之后,除了添加一个新模块之外,项目中还会发生如下的变化
- 项目根目录下的
build.gradle - 项目根目录下的
settings.gradle - 项目根目录下的
gradle.properties app模块的build.gradle
project:build.gradle
在项目根目录下的build.gradle文件中,会增加如下配置,通过apply false只声明插件版本而不实际应用它们,待需要时在app模块的build.gradle文件中再声明,后续声明时无需再注明版本号,在当前的gradle和AGP版本中
1 2 3 4
| plugins { id 'com.android.test' version '8.0.2' apply false id 'androidx.baselineprofile' version '1.2.4' apply false }
|
settings.gradle
同当下新环境的 settings.gradle
gradle.properties
同当下新环境的 gradle.properties
app:build.gradle
app模块的build.gradle文件中会增加如下配置,其中:
baselineProfile project(':baselineprofile')依赖项,让Gradle知道应该从哪个模块获取已生成的基准配置文件profileinstaller依赖项,应用首次启动时,它会读取并安装打包在APK中的基准配置文件,告诉Android Runtime(ART)哪些代码路径需要提前编译(AOT)
1 2 3 4 5 6 7 8 9 10
| plugins { id 'androidx.baselineprofile' }
dependencies { implementation 'androidx.profileinstaller:profileinstaller:1.4.1' baselineProfile project(':baselineprofile') }
|
Baseline Profile模块配置
Baseline Profile Generator模块的build.gradle文件中,简单分析一下如下配置
id 'androidx.baselineprofile'声明了这是一个测试模块
1 2 3 4
| plugins { id 'com.android.test' id 'androidx.baselineprofile' }
|
targetProjectPath指定了要生成基准配置文件的目标应用模块,animationsDisabled则是自动关闭设备动画,保证测试结果稳定
1 2 3 4 5 6 7 8
| android { targetProjectPath = ":app"
testOptions { animationsDisabled = true } }
|
在baselineProfile配置项中,启用useConnectedDevices选项,让生成器在连接的设备上运行测试,以收集基准配置文件数据
1 2 3
| baselineProfile { useConnectedDevices = true }
|
如下依赖项,其中:
benchmark-macro-junit4是基准测试框架espresso-core和uiautomator是用于编写用户界面测试的库junit是用于编写单元测试的库
1 2 3 4 5 6
| dependencies { implementation 'androidx.benchmark:benchmark-macro-junit4:1.4.1' implementation 'androidx.test.espresso:espresso-core:3.7.0' implementation 'androidx.test.ext:junit:1.3.0' implementation 'androidx.test.uiautomator:uiautomator:2.3.0' }
|
其他配置
同当下新环境的其他配置
编写关键路径采集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| @Rule public BaselineProfileRule rule = new BaselineProfileRule(); @Test public void generate() { rule.collect( TARGET_PACKAGE_NAME, 15, 3, "jqTools", true, false, s -> { Set<String> knownClasses = Set.of( "xxin.jqTools.activity.MainActivity$onCreate$1", "xxin.jqTools.activity.FlashActivity$onCreate$1" ); return !knownClasses.contains(s); }, scope -> { scope.pressHome(); scope.startActivityAndWait(); UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); device.waitForIdle(); return Unit.INSTANCE; }); }
|
生成基准配置文件
同当下新环境的生成基准配置文件
编写性能测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| @Rule public MacrobenchmarkRule rule = new MacrobenchmarkRule();
@Test public void startupCompilationNone() { benchmark(new CompilationMode.None()); }
@Test public void startupCompilationBaselineProfiles() { benchmark(new CompilationMode.Partial(BaselineProfileMode.Require)); }
private void benchmark(CompilationMode compilationMode) { rule.measureRepeated( TARGET_PACKAGE_NAME, Collections.singletonList(new StartupTimingMetric()), compilationMode, StartupMode.COLD, 10, setupScope -> { setupScope.pressHome(); return Unit.INSTANCE; }, measureScope -> { measureScope.startActivityAndWait(); return Unit.INSTANCE; } ); }
|
打包
同当下新环境的打包
参考:
- https://developer.android.com/topic/performance/baselineprofiles/overview
- https://codelabs.developers.google.com/android-baseline-profiles-improve