Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JankStats LibraryでJankを検出しよう / Detecting Jank w...

JankStats LibraryでJankを検出しよう / Detecting Jank with JankStats Library

KeitaTakahashi

July 11, 2023
Tweet

More Decks by KeitaTakahashi

Other Decks in Technology

Transcript

  1. JankStats LibraryでJankを
 検出しよう
 2023/07/11 ZOZO Tech Meetup - iOS/Android
 株式会社ZOZO


    ZOZOTOWN開発本部 ZOZOTOWNアプリ部 Android2ブロック
 Androidテックリード
 高橋啓太 Copyright © ZOZO, Inc. 1
  2. © ZOZO, Inc. 話すこと
 3 • JankStats Libraryの基本的な情報
 • Jankとは


    • Jank検出ツール
 • JankStats Libraryの基本的な使用方法

  3. © ZOZO, Inc. JankStats Libraryの基本的な情報
 4 • アプリのレンダリングパフォーマンスに関する情報を取得することができる
 ◦ 後述する「Jank」を検出可能


    • FrameMetrics APIやOnPreDrawListenerの上に構築されている
 • 2023/07/11時点ではApril 5, 2023リリースの1.0.0-alpha04が最新
 • android/nowinandroidにも入っている
 
 Metrics | Jetpack | Android Developers: https://developer.android.com/jetpack/androidx/releases/metrics android/nowinandroid | GitHub: https://github.com/android/nowinandroid

  4. © ZOZO, Inc. Jankとは
 6 遅いレンダリング | App quality |

    Android Developers: https://developer.android.com/topic/performance/vitals/render?hl=ja
 • ユーザーがスムーズにアプリを操作するためには、各デバイスで決められたリフ レッシュレートを達成できるようフレーム生成時間を維持することが必要
 ◦ 一般的には60fpsを達成することが必要
 • なんらかの理由でフレームがスキップされ、アプリの動作がスムーズでなくなるこ とを「Jank(ジャンク)」と呼ぶ

  5. © ZOZO, Inc. Jankとは - ANR等との関係性
 7 ジャンクのトラッキング | App

    quality | Android Developers: https://developer.android.com/topic/performance/vitals/tracking_jank?hl=ja
 
 遅いフレーム フリーズしたフレーム ANR 表示に要する時間 16ms~700ms 700ms~5s 5sより長い ユーザーが知覚できる現象の例 リストのスクロールがカクつく 画面遷移時がスムーズではない 画面をタップしても5秒以上反応がな い
  6. © ZOZO, Inc. Jankとは - ANR等との関係性
 8 → これらは全てJankに分類される
 遅いフレーム

    フリーズしたフレーム ANR 表示に要する時間 16ms~700ms 700ms~5s 5sより長い ユーザーが知覚できる現象の例 リストのスクロールがカクつく 画面遷移時がスムーズではない 画面をタップしても5秒以上反応がな い ジャンクのトラッキング | App quality | Android Developers: https://developer.android.com/topic/performance/vitals/tracking_jank?hl=ja
 

  7. © ZOZO, Inc. Jankとは - ANR等との関係性
 9 → これらは全てJankに分類される
  →

    Jankの検出・修正は、快適な操作感を維持するために重要
 遅いフレーム フリーズしたフレーム ANR 表示に要する時間 16ms~700ms 700ms~5s 5sより長い ユーザーが知覚できる現象の例 リストのスクロールがカクつく 画面遷移時がスムーズではない 画面をタップしても5秒以上反応がな い ジャンクのトラッキング | App quality | Android Developers: https://developer.android.com/topic/performance/vitals/tracking_jank?hl=ja
 

  8. © ZOZO, Inc. Perfetto
 11 • ADBを介して、Android端末からフレームの情報を含むパフォーマンス情報を取得 するツール
 • 取得したパフォーマンス情報はPerfetto

    UIで可視化して分析可能
 • ZOZOTOWNではスクロール時のJankを分析する際に使用した
 Perfetto: https://perfetto.dev/ perfetto | Android Developers: https://developer.android.com/studio/command-line/perfetto?hl=ja Perfettoを用いたAndroidアプリのボトルネックの特定とその改善 | ZOZO TECH BLOG: https://techblog.zozo.com/entry/android-performance-improvement-with-perfetto
  9. © ZOZO, Inc. AndroidStudio Profiler
 12 • Chipmunk以降のAndroid StudioではProfilerでJankを検出可能
 ◦

    Android12以降の端末が必要
 
 Android Developers Japan Blog: Android Studio の CPU profiler で UI のジャンクを検出する https://android-developers-jp.googleblog.com/2022/07/spot-your-ui-jank-using-cpu-profiler-in-android-studio.html
 
 

  10. © ZOZO, Inc. JankStats Library
 13 • androidx.metrics.performance
 • 下記の機能を提供する


    ◦ Jankの識別
 ▪ Jankの発生タイミングに関する情報を取得できる
 ◦ Jankが発生した時点でのアプリの状態取得
 ▪ Jankが発生した際にユーザーがどのような操作をしていたかを確認できる
 ◦ 結果のレポート
 ▪ フレームに関する情報(完了にかかった時間など)を取得できる

  11. © ZOZO, Inc. JankStats Libraryの何が良いのか?
 14 • 開発環境でのJank検出はPerfettoやAndroid StudioのProfilerで十分可能
 •

    しかし、本番環境(ユーザーの手元)で実際に発生しているJankを検出することは できない
 • JankStats Libraryをプロダクトに導入し、データを収集することで、本番環境で発 生しているJankの原因が分析可能になる
 ◦ 取得したデータの保存やデータを送信する仕組みは提供されていない
 ※「保存とアップロードに関する詳細情報は、JankStats API アルファ版リリースでは提供されません」
 とあるので将来的になんらかのサポートが追加される可能性はある
 https://developer.android.com/topic/performance/jankstats?hl=ja
 

  12. © ZOZO, Inc. class MainActivity : ComponentActivity() { private lateinit

    var jankStats: JankStats override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ... } jankStats = JankStats.createAndTrack(window) { frameData -> if (frameData.isJank) { // Jankと判定されたフレームかどうか Log.v("Jank detected!", frameData.toString()) } } } } 基本的な使用方法 - 初期化
 18 JankStats.createAndTrackでJankStatsのインスタンスを生成
 

  13. © ZOZO, Inc. class MainActivity : ComponentActivity() { private lateinit

    var jankStats: JankStats override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ... } jankStats = JankStats.createAndTrack(window) { frameData -> if (frameData.isJank) { // Jankと判定されたフレームかどうか Log.v("Jank detected!", frameData.toString()) } } } } 基本的な使用方法 - 初期化
 
 JankStats.createAndTrackでJankStatsのインスタンスを生成
 
 19 JankStatsのインスタンス生成時にdecorViewが生成されていない場合、 IllegalStateExceptionが発生するので注意
  14. © ZOZO, Inc. 基本的な使用方法 - 初期化
 
 20 class MainActivity

    : ComponentActivity() { private lateinit var jankStats: JankStats override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ... } jankStats = JankStats.createAndTrack(window) { frameData -> if (frameData.isJank) { // Jankと判定されたフレームかどうか Log.v("Jank detected!", frameData.toString()) } } } } JankStats.createAndTrackの第二引数には、フレーム情報を受け取るコールバック (JankStats.OnFrameListener)を渡す

  15. © ZOZO, Inc. 21 isTrackingEnabledでフレーム情報収集の有効/無効を切り替える(デフォルト値はtrue)
 class MainActivity : ComponentActivity() {

    ... override fun onResume() { super.onResume() ... jankStats.isTrackingEnabled = true } override fun onPause() { super.onPause() jankStats.isTrackingEnabled = false } } 基本的な使用方法 - 初期化
 

  16. © ZOZO, Inc. 基本的な使用方法 - FrameData
 23 • frameStartNanos
 ◦

    フレームの開始時刻(ns)
 • frameDurationUiNanos
 ◦ フレームの長さ(ns)
 • isJank
 ◦ ジャンクかどうかを示すフラグ
 • states
 ◦ アプリの状態 (StateInfo)
 JankStats ライブラリ | App quality | Android Developers: https://developer.android.com/topic/performance/jankstats?hl=ja#reporting
 
 

  17. © ZOZO, Inc. 基本的な使用方法 - FrameData
 24 • frameStartNanos
 ◦

    フレームの開始時刻(ns)
 • frameDurationUiNanos
 ◦ フレームの長さ(ns)
 • isJank
 ◦ ジャンクかどうかを示すフラグ
 • states
 ◦ アプリの状態 (StateInfo)
 JankStats ライブラリ | App quality | Android Developers: https://developer.android.com/topic/performance/jankstats?hl=ja#reporting
 
 

  18. © ZOZO, Inc. @Composable fun rememberMetricsStateHolder(): PerformanceMetricsState.Holder { val view

    = LocalView.current return remember(view) { PerformanceMetricsState.getHolderForHierarchy(view) } } 基本的な使用方法 - PerformanceMetricsState
 27 PerformanceMetricsState.Holderを使用する

  19. © ZOZO, Inc. 基本的な使用方法 - PerformanceMetricsState
 28 @Composable fun JankyScreen()

    { val holder = rememberMetricsStateHolder() val key = "画面名" LaunchedEffect(holder) { // 状態を設定 holder.state?.putState(key, "JankyScreen") } JankyLazyColumn() // Jankが発生するComposable } PerformanceMetricsState#putStateにkey-value形式で状態をアプリの設定する

  20. © ZOZO, Inc. 基本的な使用方法 - PerformanceMetricsState
 31 @Composable fun JankyScreen()

    { ... Column { Button(onClick = { // 無効になった状態を削除 holder.state?.removeState(key) }) { Text(text = "状態削除") } JankyLazyColumn() } }
  21. © ZOZO, Inc. 基本的な使用方法 - nowinandroidの場合
 34 nowinandroid | NiaAppState.kt: 

    https://github.com/android/nowinandroid/blob/main/app/src/main/java/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt
 @Composable private fun NavigationTrackingSideEffect(navController: NavHostController) { TrackDisposableJank(navController) { metricsHolder -> val listener = NavController.OnDestinationChangedListener { _, destination, _ -> metricsHolder.state?.putState("Navigation", destination.route.toString()) } navController.addOnDestinationChangedListener(listener) onDispose { navController.removeOnDestinationChangedListener(listener) } } }
  22. © ZOZO, Inc. 基本的な使用方法 - nowinandroidの場合
 35 nowinandroid | JankStatsExtensions.kt: 

    https://github.com/android/nowinandroid/blob/4d65946f95f55505a21a1651c2fd5587cd5bb533/core/ui/src/main/java/com/google/samples/apps/nowinandroid/core/ui/JankStatsExtensions.kt @Composable fun TrackScrollJank(scrollableState: ScrollableState, stateName: String) { TrackJank(scrollableState) { metricsHolder -> snapshotFlow { scrollableState.isScrollInProgress }.collect { isScrollInProgress -> metricsHolder.state?.apply { if (isScrollInProgress) { putState(stateName, "Scrolling=true") } else { removeState(stateName) } } } } }
  23. © ZOZO, Inc. JankStats Libraryの使用方法まとめ
 36 • フレーム情報の収集はコールバックを登録するだけなのでお手軽
 • フレームの情報とアプリの状態を紐づけるには

    PerformanceMetricsState#putStateを使用する
 • 無効になったアプリの状態はremoveStateで削除する
 • 状態管理にはDisposableEffectやLifecycleObserverを活用するのが良さそう
 ※ putSingleFrameStateという1フレーム分の状態を登録し、自動で削除するAPIもあります(使い所が難しい)
 ※
  24. © ZOZO, Inc. まとめと感想
 38 • Jankとはフレームがスキップされ、アプリがスムーズでなくなることを指す
 • JankStats Libraryは本番環境で使用することで価値を発揮する


    ◦ ユーザーの手元で発生しているJankが検出可能になる
 ◦ Jankの分析に有用なデータを収集するためには、アプリのどのような情報をフ レームの情報に乗せるかが重要になりそう