先来了解一下几种应用启动的概念:
冷启动(Cold Start):应用完全未启动,模拟全新启动过程。
暖启动(Warm Start) :进程仍然存活,但 Activity
可能需要重新创建。
热启动(Hot Start) :进程存活,Activity
只是从 onStop
恢复到 onResume
adb命令检测
shell
adb shell am start -W com.example.quickdev/com.example.quickdev.ui.activity.SplashActivity
或者使用:
shell
adb shell am start -S -W com.example.quickdev/.ui.activity.SplashActivity
-S
参数会强制停止目标 Activity -W
:等待启动完成并打印耗时(关键参数)
- 执行结果-冷启动
shell
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.quickdev/.ui.activity.SplashActivity }
Status: ok
LaunchState: COLD
Activity: com.example.quickdev/.ui.activity.SplashActivity
TotalTime: 1573
WaitTime: 1575
Complete
- 执行结果-热启动
shell
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.quickdev/.ui.activity.SplashActivity }
Warning: Activity not started, its current task has been brought to the front
Status: ok
LaunchState: HOT
Activity: com.example.quickdev/.ui.activity.HomeActivity
TotalTime: 136
WaitTime: 138
Complete
- 执行结果-暖启动
shell
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example.quickdev/.ui.activity.SplashActivity }
Status: ok
LaunchState: WARM
Activity: com.example.quickdev/.ui.activity.SplashActivity
TotalTime: 259
WaitTime: 262
Complete
字段 | 含义 | 单位 | 备注 |
---|---|---|---|
Status | 启动状态 | - | ok 表示成功,error 表示失败 |
LaunchState | 启动类型 | - | COLD (冷启动)/WARM (温启动)/HOT (热启动) |
Activity | 实际启动的 Activity | - | 可能被 Intent Filter 重定向 |
TotalTime | Activity 自身启动耗时 | 毫秒(ms) | 从创建到首帧完成 |
WaitTime | 系统总等待时间 | 毫秒(ms) | 包括系统资源准备时间 |
Complete | 完成标记 | - | 仅表示命令执行完毕 |
启动类型(LaunchState)
- COLD:完全冷启动(进程不存在)
- WARM:温启动(进程存在但Activity被销毁)
- HOT:热启动(Activity仍在栈中)
时间统计原理
TotalTime
:通过ActivityRecord
的windowsDrawn
时间戳计算WaitTime
:包含 AMS(ActivityManagerService)调度时间
WaitTime
包含系统调度开销(如 CPU 竞争),所以总是大于或等于TotalTime
BenchmarkRule
BenchmarkRule
是 Android Benchmark 库中的一个 JUnit 规则(@Rule
),用于在 单元测试(JUnit4)环境下进行性能基准测试 。它能够测量代码片段的执行时间,并提供 抖动控制 和 精确的 CPU 时间测量 ,适用于 方法级别的性能评估。
使用要求
- Android 10以上设备运行
- 装机应用为release版本
- 测试应用需要在Manifest里面声明:
xml
<application>
<profileable android:shell="true"/>
</application>
- 运行时需在主项目
build.gradle
指定androidx.benchmark.junit4.AndroidBenchmarkRunner
作为 runner
gradle
android {
defaultConfig {
//testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 替换掉这个
testInstrumentationRunner "androidx.benchmark.junit4.AndroidBenchmarkRunner"
}
}
- 运行设备要为 userdebug 模式,可 root
Macrobenchmark(宏基准测试)
在项目根目录 New -> Moudle 选择 benchmark 、Macrobenchmark ,Finsh 创建宏基准测试模块。
定义 宏基准测试主要关注整个应用程序或大部分应用功能的性能表现。它通常测试的是较大的操作或完整的业务流程,比如用户登录、数据加载、界面渲染等。
目的 衡量应用程序在真实使用场景下的总体性能,通常关注用户的整体体验。
示例 1.测试整个应用启动的时间。
- 测试一个完整的用户交互流程的响应时间,比如从点击按钮到完成操作的时间。
- 测试应用加载某个大数据集的性能。
当模块创建完毕后,会在清单文件自动生成
xml
<profileable
android:shell="true"
tools:targetApi="29" />
和主项目的build.gradle 中
gradle
benchmark {
initWith release
matchingFallbacks = ['release']
signingConfig signingConfigs.release //++
}
其中 signingConfig signingConfigs.release
是后面要加的,代表运行的时候指定签名配置。否则会报错: Exception thrown during onBeforeAll invocation of plugin AndroidTestApkInstallerPlugin: ErrorName: INSTALL_PARSE_FAILED_NO_CERTIFICATES
其他的配置不需要更改,benchmark 模块默认配置如下:
xml
buildTypes {
// This benchmark buildType is used for benchmarking, and should function like your
// release build (for example, with minification on). It's signed with a debug key
// for easy local/CI testing.
benchmark {
debuggable = true
signingConfig = debug.signingConfig
matchingFallbacks = ["release"]
}
}
看别人的 debuggable
不能为true
,不过我的为true
好像也没什么影响。
启动测试
先打包release版本apk,安装进可 root 设备。接着在 benchmark 模块下的 ExampleStartupBenchmark
类中 启动 startup
方法,左边有个小箭头直接启动即可。
代码介绍如下:
java
@Test
public void startup() {
// 调用 mBenchmarkRule.measureRepeated 方法进行重复测量,记录应用启动性能
mBenchmarkRule.measureRepeated(
// 第一个参数是包名,指定测试的应用包名
"com.example.quickdev",
// 第二个参数是要收集的性能指标,这里使用的是 StartupTimingMetric 来度量启动时间
Collections.singletonList(new StartupTimingMetric()),
// 第三个参数是编译模式,DEFAULT 表示使用默认编译模式
CompilationMode.DEFAULT,
// 第四个参数是启动模式,COLD 表示冷启动,意味着应用从完全退出状态启动
StartupMode.COLD,
// 第五个参数是测量次数,这里指定了测量 5 次
5,
// 第六个参数是一个 Lambda 表达式,表示测量期间需要执行的操作
scope -> {
// 按下 Home 键,模拟用户离开应用并返回桌面
scope.pressHome();
// 启动并等待指定应用,等待应用启动完成后再继续执行后续操作
scope.startActivityAndWait();
// 返回 null,表示操作执行完毕
return null;
});
}
冷启动测试结果
暖启动测试结果
热启动测试结果
可点击右侧测试次数的下标,点进去查看详细的测试信息,在第二张图的左上角可以查看cpu的消耗,左下找到当前应用行,查看代码具体耗时信息,右侧
Frames Chart
可查看代码耗时火焰图展示书数据等。可以分析 CPU & 线程使用情况(避免 主线程阻塞)、查看火焰图,找到性能瓶颈(减少不必要的方法调用)
📊 BenchmarkRule vs. Macrobenchmark
BenchmarkRule
→ 方法级测试,适用于小型、局部代码性能分析(如字符串拼接、循环优化)。Macrobenchmark
→ UI 级测试,适用于整个应用的性能分析(如 Activity 启动时间、RecyclerView 滚动性能)。