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 等工具檢視中:
- 一看就懂!圖解 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 再見!
- 面試必備:Kotlin 執行緒同步的 N 種方法
- Jetpack MVVM 七宗罪之六:ViewModel 介面暴露不合理