Google I/O :Android Jetpack 最新变化(二) Performance
theme: devui-blue highlight: atelier-sulphurpool-light
前言
本系列文章从 Architecture,UI,Performance 和 Compose 这四个方向带大家了解本次 I/O 上 Jetpack 的最新内容。
- Google I/O :Android Jetpack 最新变化(一)Architecture
- Google I/O :Android Jetpack 最新变化(二)Performance
- Google I/O :Android Jetpack 最新变化(三)UI
- Google I/O :Android Jetpack 最新变化(四)Compose
本文是第二篇:Performance 篇。
1. JankStats 卡顿检测
JankStats 用来追踪和分析应用性能,发现 Jank 卡顿问题,它最低向下兼容到 API 16,可以在绝大多数机器设备上使用,有了它我们不必再求助 BlockCanery 等三方工具了。
groovy
implementation "androidx.metrics:metrics-performance:1.0.0-alpha01"
我们需要为每个 Window 创建一个 JankStats 实例,并通过 OnFrameListener 回调获取包含是否卡顿在内的帧信息,示例如下:
```kotlin
class JankLoggingActivity : AppCompatActivity() {
private lateinit var jankStats: JankStats
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ...
// metricsStateHolder可以收集环境信息,跟随帧信息返回
val metricsStateHolder = PerformanceMetricsState.getForHierarchy(binding.root)
// 基于当前 Window 创建 JankStats 实例
jankStats = JankStats.createAndTrack(
window,
Dispatchers.Default.asExecutor(),
jankFrameListener,
)
// 设置 Activity 名字到环境信息
metricsStateHolder.state?.addState("Activity", javaClass.simpleName)
// ...
}
private val jankFrameListener = JankStats.OnFrameListener { frameData ->
// 监听到的帧信息
Log.v("JankStatsSample", frameData.toString())
}
}
PerformanceMetricsState 用来收集你希望跟随 frameData 一起返回的状态信息,比如上面例子中设置了当前 Activity 名称,下面是 frameData 的打印日志:
txt
JankStats.OnFrameListener: FrameData(frameStartNanos=827233150542009, frameDurationUiNanos=27779985, frameDurationCpuNanos=31296985, isJank=false, states=[Activity: JankLoggingActivity])
```
更多参考:http://medium.com/androiddevelopers/jankstats-goes-alpha-8aff942255d5
2. Baseline Profiles 基准配置
Android 8.0 之后默认开启 ART 虚拟机。ART 最初版本在安装应用时会对全部代码进行 AOT 预编译,将字节码转换为机器码存在本地,这提升了运行时的速度,但是会导致安装过程变慢。因此后来 ART 改进为 JIT 和 AOT 相结合的方式,在应用安装时只将热点代码编译成机器码,缩短安装时间。
Baselin Profiles 基准配置文件允许我们配置哪些代码成为热点代码。基准配置文件将在 APK 的 assets/dexopt/baseline.prof
中编译为二进制形式,例如如果我们想提升首帧的性能,可以将应用启动或帧渲染期间使用的方法配置到 prof 文件中。
prof 文件可以通过自动或手动方式生成,我们可以编写 JUnit4 测试用例,通过执行 BaselineProfileRule 在测试中发现待优化的瓶颈代码,并生成对应的 prof 文件
```kotlin @ExperimentalBaselineProfilesApi @RunWith(AndroidJUnit4::class) class BaselineProfileGenerator { @get:Rule val baselineProfileRule = BaselineProfileRule()
@Test
fun startup() =
baselineProfileRule.collectBaselineProfile(packageName = "com.example.app") {
pressHome()
startActivityAndWait()
}
}
我们也可以手动创建 prof 文件,只需遵循一些简单的语法规则。例如下面展示了 Jetpack Compose 库中包含的一些 Prof 规则,
txt
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;
``
上述配置遵循
[FLAGS][CLASS_DESCRIPTOR]->[METHOD_SIGNATURE]` 格式,其中 FLAGS 中的 H/S/P 代表方法的调用实际,比如是否是启动时调用等。
更多参考:http://android-developers.googleblog.com/2022/01/improving-app-performance-with-baseline.html
3. Benchmark 基准测试
Jetpack 当前提供了两套 Benchmark 库,Microbenchmark 和 Macrobenchmark (微基准和宏基准),分别用于不同场景下的基准测试。
Mircobenchmark 的测试对象是代码块,它的依赖如下:
groovy
androidTestImplementation 'androidx.benchmark:benchmark-junit4:1.1.0-beta03'
我们可以在 JUnit4 中应用 BenchmarkRule,示例如下:
```kotlin
@RunWith(AndroidJUnit4::class)
class SampleBenchmark {
@get:Rule
val benchmarkRule = BenchmarkRule()
@Test
fun benchmarkSomeWork() {
benchmarkRule.measureRepeated {
doSomeWork() //执行待测试代码
}
}
} ```
Macrobenchmark 通常面向更大粒度的场景测试,例如一个 Activity 启动或者一个用户操作等。由于 Macrobenchmark 不进行代码级别测试,我们可以创建独立于业务代码的单独模块进行测试:
下面展示了使用 MacrobenchmarkRule 测试一个 Activity 的启动: ```kotlin @get:Rule val benchmarkRule = MacrobenchmarkRule()
@Test
fun startup() = benchmarkRule.measureRepeated(
packageName = "mypackage.myapp",
metrics = listOf(StartupTimingMetric()),
iterations = 5,
startupMode = StartupMode.COLD
) { // this = MacrobenchmarkScope
pressHome()
val intent = Intent()
intent.setPackage("mypackage.myapp")
intent.setAction("mypackage.myapp.myaction")
startActivityAndWait(intent)
}
```
配合 2021.1.1 或更高版本的 Android Studio ,Benchmark 的测试结果会直接显示在 IDE 窗口中。
当然,测试结果也可以导出为 JSON 格式
更多参考:http://medium.com/androiddevelopers/measure-and-improve-performance-with-macrobenchmark-560abd0aa5bb
4. Tracing 事件追踪
Tracing 用来在代码添加 trace 信息,trace 信息可以显示在 Systrace 和 Perfetto 等工具中。
groovy
implementation "androidx.tracing:tracing:1.1.0-beta01"
下面的例子汇总,我们通过 Trace 类的 benginSection/endSection 方法追踪 onCreateViewHolder 和 onBindViewHolder 方法执行的起始点
```kotlin
class MyAdapter : RecyclerView.Adapter
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
Trace.beginSection("MyAdapter.onBindViewHolder")
try {
try {
Trace.beginSection("MyAdapter.queryDatabase")
val rowItem = queryDatabase(position)
dataset.add(rowItem)
} finally {
Trace.endSection()
}
holder.bind(dataset[position])
} finally {
Trace.endSection()
}
}
} ``` 需要注意 benginSection/endSection 必须成对出现,且必须在同一线程中。我们 Trace 的 section 会作为新增的自定义事件出现在 Perfetto 等工具视图中:
- Android Studio Electric Eel 起支持手机投屏
- Compose 为什么可以跨平台?
- 一看就懂!图解 Kotlin SharedFlow 缓存系统
- 深入浅出 Compose Compiler(2) 编译器前端检查
- 深入浅出 Compose Compiler(1) Kotlin Compiler & KCP
- Jetpack MVVM七宗罪之三:在 onViewCreated 中加载数据
- 为什么说 Compose 的声明式代码最简洁 ?Compose/React/Flutter/SwiftUI 语法对比
- Compose 类型稳定性注解:@Stable & @Immutable
- Fragment 这些 API 已废弃,你还在使用吗?
- 告别KAPT!使用 KSP 为 Kotlin 编译提速
- 探索 Jetpack Compose 内核:深入 SlotTable 系统
- 盘点 Material Design 3 带来的新变化
- Compose 动画边学边做 - 夏日彩虹
- Google I/O :Android Jetpack 最新变化(二) Performance
- Google I/O :Android Jetpack 最新变化(一) Architecture
- Google I/O :Android Jetpack 最新变化(四)Compose
- Google I/O :Android Jetpack 最新变化(三)UI
- 一文看懂 Jetpack Compose 快照系统
- 聊聊 Kotlin 代理的“缺陷”与应对
- AAB 扶正!APK 再见!