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

レンダリングしていますを改善する

Naoki Ishii
September 28, 2018

 レンダリングしていますを改善する

Naoki Ishii

September 28, 2018
Tweet

More Decks by Naoki Ishii

Other Decks in Programming

Transcript

  1. レンダリングしていますを
    改善する

    View full-size slide

  2. 自己紹介
    ▣ 石井直貴
    □ NAVITIME 2010〜
    ▣ Androidアプリ開発
    □ NAVITIME, 乗換NAVITIME
    □ ここ地図 ( 8月リリース )
    ▣ 趣味
    □ ライブ鑑賞
    □ 個人アプリ開発

    View full-size slide

  3. 今日話すこと
    ▣ レンダリングしています
    ▣ 調査方法
    ▣ 改善したこと
    ▣ まとめ

    View full-size slide

  4. 注:
    ▣ パフォーマンス改善中です
    ▣ 今後のアップデートにご期待下さい!

    View full-size slide

  5. レンダリングしています

    View full-size slide

  6. レンダリングしています

    View full-size slide

  7. レンダリングしています
    ▣ Android Vitalsの指標の1つ
    ▣ 主な指標には入っていない
    □ 主な指標
    ■ 過剰なウェイクアップ
    ■ クラッシュ発生率
    ■ ANR発生率
    ▣ 遅い表示・フリーズしたフレームがある

    View full-size slide

  8. 遅い表示
    50% を超えるフレームで表示に要する時間が 16 ミリ秒を
    上回った 1 日のセッションの割合(%)です。
    1000ms / 60frames
    = 16.6666ms / frame

    View full-size slide

  9. フリーズしたフレーム
    0.1% を超えるフレームで表示に要する時間が 700 ミリ秒
    を上回った 1 日のセッションの割合(%)です。

    View full-size slide

  10. レンダリングしています - まとめ
    ▣ わかる数値
    □ アプリ全体で遅いレンダリング・フリーズしたフレームの継続的
    な数値
    ▣ できること
    □ この数値を追うことで、リリース毎にレンダリングパフォーマンス
    を意識することができる

    View full-size slide

  11. どこの画面が遅いのかを知る

    View full-size slide

  12. どこの画面が遅いのか
    Firebase Performance Monitoring

    View full-size slide

  13. Firebase Performance Monitoring
    ▣ 2018 Google I/O 〜 betaを抜けてGAに
    ▣ Activityの計測は自動

    View full-size slide

  14. Firebase Performance
    ▣ Activityの自動トレース
    ▣ build.gradleに記述するだけ
    classpath 'com.google.firebase:firebase-plugins:1.1.5'
    build.gradle (project)
    apply plugin: 'com.google.firebase.firebase-perf'
    implementation 'com.google.firebase:firebase-perf:16.1.0'
    build.gradle (app)

    View full-size slide

  15. Firebase Performance
    ▣ Activity毎に遅いレンダリング・フリーズしたフレームが
    出る

    View full-size slide

  16. どこの画面が遅いのか
    MultiActivity-App 1Activity-App

    View full-size slide

  17. どこの画面が遅いのか (1Activity-App)

    View full-size slide

  18. どこの画面が遅いのか (1Activity-App)
    ▣ Firebase Performanceのカスタムトレースを使う

    View full-size slide

  19. Trace
    myTrace : Trace = FirebasePerformance.startTrace("test_trace")
    item : Item = cache.fetch("item")
    if (item != null) {
    myTrace.incrementMetric("item_cache_hit", 1)
    }
    myTrace.stop()
    開始
    カウント
    停止

    View full-size slide

  20. Activityの自動計測をどのようにやっているか
    com.google.android.gms.internal.firebase-perf.zze
    ▣ ActivityLifecycleCallbacks使ってる
    ▣ onActivityStarted / onActivityStoppedで計測開始/停

    ▣ 16(ms), 700(ms)の条件でカウントしている
    これっぽい。。

    View full-size slide

  21. やっている処理
    ▣ onActivityStarted
    □ Traceをstart
    □ FrameMetricsAggregatorにActivityをAdd
    ▣ onActivityStopped
    □ FrameMetricsAggregatorからActivityをremoveし計測
    値を取得
    □ 取得した計測値を加工してTraceのカウントを増やす
    □ Traceをstop

    View full-size slide

  22. FrameMetrics
    ▣ FrameMetricsListener API
    □ UI レンダリング パフォーマンスを監視
    □ adb shell dumpsys gfx infoと同じ内容.
    ■ 最新の120 フレームに制限されない
    ■ adb 接続不要
    □ Android7.0(N)から
    ■ そのため、AndroidVitalsではOS7.0以上の値しか表示
    されない(6系も選択できるが)
    https://developer.android.com/about/versions/nougat/android-7.0?hl=ja#framemetrics_api

    View full-size slide

  23. FrameMetricsAggregator
    ▣ FrameMetricsをまとめた値を取得できる
    ▣ Support Library 26.1.0〜

    View full-size slide

  24. FragmentLifecycleCallbacksで実装
    override fun onFragmentStarted(fm: FragmentManager, f: Fragment) {
    super.onFragmentStarted(fm, f)
    val frameMetricsAggregator = FrameMetricsAggregator().apply {
    add(f.requireActivity())
    }
    val trace = FirebasePerformance.startTrace(f.javaClass.simpleName)
    fragmentAggregatorMap[f] = frameMetricsAggregator
    fragmentTraceMap[f] = trace
    }
    https://github.com/iiinaiii/AndroidPerformanceSample
    FragmentPerformanceTracer.kt

    View full-size slide

  25. override fun onFragmentStopped(fm: FragmentManager, f: Fragment) {
    ・・・
    val collectedMetrics = frameMetricsAggregator?.remove(f.requireActivity())
    collectedMetrics?.get(0)?.let { totalDuration ->
    for (i in 0 until totalDuration.size()) {
    val frameDuration = totalDuration.keyAt(i)
    val sampleNum = totalDuration.valueAt(i)
    if (frameDuration > 700) {
    frozenFrameCount += sampleNum
    }
    if (frameDuration > 16) {
    slowRenderingCount += sampleNum
    }
    }
    }
    // 遅いレンダリングだったフレーム数
    if (slowRenderingCount > 0) {
    trace?.incrementMetric(SLOW_RENDERING_COUNT, slowRenderingCount.toLong())
    }
    trace?.stop()
    }
    metrics : SparseArray
    {6=3, 7=11, 8=1, 9=1, 11=1,12=1,
    25=1, 40=1, 87=1}
    6ms → 3frame
    7ms → 11frame
    8ms → 1frame

    View full-size slide

  26. レンダリングしています - Fragment
    ▣ 「時間」に出力

    View full-size slide

  27. ▣ 詳細で確認可
    □ フレーム数
    □ フリーズした
    フレーム数
    □ フリーズした
    フレームの割合
    □ 遅いレンダリングフ
    レーム数
    □ 遅いレンダリングフ
    レームの割合
    フレーム数 フリーズしたフ
    レーム数
    フリーズしたフレー
    ムの割合(%)
    遅いレンダリン
    グフレーム数
    遅いレンダリン
    グフレームの割
    合(%)
    レンダリングしています - Fragment

    View full-size slide

  28. ▣ 一覧に遅いレンダリング・フリーズしたフレームが出ないので
    画面間の比較がしづらい
    ▣ 詳細に行けば見れるので、画面毎の数値が見れるのは良さそ

    ▣ Activity.getWindowに対してのFrameMetricsなので、
    Fragment単体でのレンダリング数値でないことに注意
    ▣ どの画面(Fragment)が出ているときに数値が悪いかはわか

    レンダリングしています - Fragment

    View full-size slide

  29. どの画面が遅いのかを知る - まとめ
    ▣ わかる数値
    □ 画面単位で遅い箇所
    ▣ できること
    □ サンプル数と、遅いレンダリング or フリーズしたフレームの値か
    ら、改修優先度を決めることができる

    View full-size slide

  30. 画面内のどこが遅いのかを知る

    View full-size slide

  31. 画面内のどこが遅いか
    ▣ Hierarchy Viewer
    ▣ GPUレンダリングのプロファイル
    ▣ Systrace

    View full-size slide

  32. Systrace
    https://developer.android.com/studio/command-line/systrace
    ● platform-tools配下にあるツール
    ● CPU利用状況やUIスレッドの処理内容/時間が見れる
    ● Android Studio 3.2〜 Profilerで見れる

    View full-size slide

  33. Systrace - 調査の流れ
    ● Alertをチェック
    ● 問題のある箇所がハイラ
    イト

    View full-size slide

  34. ● 遅いFrameをチェック
    ●    > 16.6ms
    Systrace - 調査の流れ
    ● 遅い処理を調査

    View full-size slide

  35. 問題のある箇所の修正
    ● Expensive measure/layout pass
    → レイアウト階層の見直しとか
    ● Long View#draw()
    → CustomViewのonDrawで時間のかかる処理して
    ないかとか

    View full-size slide

  36. Systrace - favicon
    ▣ 遅いFrameの割合で
    faviconの色が違う
    □ 赤 → 30%より大きい
    □ 黃 → 5% 〜 30%
    □ 緑 → 5%以下
    const portionBad = badFramesObserved / framesObserved;
    if (portionBad > 0.3) {
    this.model.faviconHue = 'red';
    } else if (portionBad > 0.05) {
    this.model.faviconHue = 'yellow';
    } else {
    this.model.faviconHue = 'green';
    }
    https://github.com/catapult-project/catapult/blob/maste
    r/tracing/tracing/extras/android/android_auditor.html

    View full-size slide

  37. override fun bind(viewBinding: RouteResultItemFooterBinding, position: Int) {
    try {
    Trace.beginSection("RouteFooterItem.bind")
    registerAdditionalInfoReceiver(viewBinding.root.context)
    try {
    Trace.beginSection("RouteFooterItem.bind.setupAroundCoupon")
    setupAroundCoupon(viewBinding)
    } finally {
    Trace.endSection()
    }
    ・・・
    } finally {
    Trace.endSection()
    }
    }
    android.os.Trace
    ● 開始 → Trace.beginSection(“名前”)
    ● 停止 → Trace.endSection()
    Systrace - 注目したい場所に名前を付ける

    View full-size slide

  38. Systrace - 注目したい場所に名前を付ける
    RouteFooterItem.bind
    RouteFooterItem.bind.setupAroundCoupon

    View full-size slide

  39. Systrace - 操作
    いっぱいある。。

    View full-size slide

  40. Systrace - 操作
    Select mode
    ● 要素の選択
    ● cmd or dragで複数要素選択
    ● ダブルクリックで同じ名前の要素全選択
    基本的にはこのモードだけでOK
    他のモードはショートカットで代替する
     (mode切替が煩わしいため)

    View full-size slide

  41. Systrace - 操作
    Pan
    ● Viewの上下左右移動
    ● ショートカットキー
    ○ a → 左へ移動
    ○ d → 右へ移動
    ○ shift + a/d で大きく移動
    ○ 上下はスクロールで
    Zoom
    ● ズーム
    ● ショートカットキー
    ○ w → ズームイン
    ○ s → ズームアウト
    ○ f → 選択した要素にフォーカスズーム

    View full-size slide

  42. Systrace - 操作
    Timing
    ● ハイライト
    ● その箇所にかかった時間が見れる
    ● ショートカットキー
    ○ m → 選択している要素のハイライト

    View full-size slide

  43. 画面内のどこが遅いのかを知る - まとめ
    ▣ わかる数値
    □ どの処理が遅いか
    ▣ できること
    □ 具体的なコードの改修ができる

    View full-size slide

  44. 改善したこと
    ルート結果画面を
    RecyclerView化しました

    View full-size slide

  45. ルート結果のレンダリング改善
    Before
    After

    View full-size slide

  46. RecyclerView化に際して
    ▣ 既存画面のリファクタリング(表示パターン多)
    □ Groupie
    □ 表示要素毎にItem分け. 表示パターンはItem内で吸収.
    ▣ パフォーマンス
    □ RecycledViewPool
    □ ViewTypeを分けすぎないようまとめた

    View full-size slide

  47. レンダリングしていますを改善する - まとめ
    ▣ スケール別のレンダリング数値確認方法とできること
    □ アプリ全体の数値
    ■ Android Vitals
    ■ リリース毎にレンダリングパフォーマンスを意識できる
    □ どの画面が遅いか
    ■ Firebase Performance Monitoring
    ■ 画面単位での改修優先度を決めることができる
    □ どの処理が遅いか
    ■ Systrace(など)
    ■ 具体的なコードの改修ができる

    View full-size slide

  48. レンダリングしていますを改善する - まとめ
    ▣ 改善例
    □ ルート結果の例

    View full-size slide