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

NAVITIMEアプリでのパフォーマンス改善の取り組み

 NAVITIMEアプリでのパフォーマンス改善の取り組み

3/27(水)に開催されたDevLOVE様主催のイベント『ナビタイムの現場 第2経路 -ナビタイムを支える技術-』にて発表した資料です。

NAVITIME JAPAN

March 27, 2019
Tweet

More Decks by NAVITIME JAPAN

Other Decks in Technology

Transcript

  1. 自己紹介 ▣ 石井直貴 □ 株式会社ナビタイムジャパン新卒入社 2010〜 ▣ Androidアプリ開発 □ NAVITIME,

    乗換NAVITIME ▣ 趣味 □ ライブ鑑賞 □ 個人アプリ開発 (Flutterはじめました)
  2. Why 60fps? • 12fps ◦ アニメーションに感じる • 24 - 30

    fps ◦ 流れるように感じる (ビジュアルエフェクト等の力を借りて) ◦ 映画業界でメジャー • 60fps ◦ エフェクト無しでスムーズに感じる ◦ スイートスポット
  3. Jank • 60fps ◦ 1000ms / 60frames = 16.666ms/frame •

    レンダリングが間に合わない場合”Jank”が発生する • Jank ◦ フレームのスキップ ◦ アニメーションの省略や動作のカクつきを感じる
  4. Android Vitalsでわかること - まとめ ▣ わかる数値 □ アプリ全体で遅いレンダリング・フリーズしたフレームの継続的 な数値 ▣

    できること □ この数値を追うことで、リリース毎にレンダリングパフォーマンス を意識することができる
  5. 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)
  6. Trace myTrace : Trace = FirebasePerformance.startTrace("test_trace") item : Item =

    cache.fetch("item") if (item != null) { myTrace.incrementMetric("item_cache_hit", 1) } myTrace.stop() 開始 カウント 停止
  7. やっている処理 ▣ onActivityStarted □ Traceをstart □ FrameMetricsAggregatorにActivityをAdd ▣ onActivityStopped □

    FrameMetricsAggregatorからActivityをremoveし計測 値を取得 □ 取得した計測値を加工してTraceのカウントを増やす □ Traceをstop
  8. 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
  9. 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
  10. override fun onFragmentStopped(fm: FragmentManager, f: Fragment) { ・・・   var totalFrameCount

    = 0   var slowRenderingCount = 0   var frozenFrameCount = 0 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)     totalFrameCount += sampleNum if (frameDuration > 700) { frozenFrameCount += sampleNum } if (frameDuration > 16) { slowRenderingCount += sampleNum } } } ・・・ 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 …
  11. //  トータルフレーム数   if (totalFrameCount > 0) { trace?.incrementMetric(TOTAL_FRAME_COUNT, totalFrameCount.toLong())   }

    // 遅いレンダリングだったフレーム数 if (slowRenderingCount > 0) { trace?.incrementMetric(SLOW_RENDERING_COUNT, slowRenderingCount.toLong()) } // 遅いレンダリングだったフレームの割合 (%) if (totalFrameCount > 0 && slowRenderingCount > 0) { val slowRenderingRatio: Float = slowRenderingCount.toFloat() / totalFrameCount.toFloat() * 100 trace?.incrementMetric(SLOW_RENDERING_RATIO, slowRenderingRatio.toLong()) } trace?.stop() } Firebase Performance自動計測に近いもの
  12. ▣ 詳細で確認可 □ フレーム数 □ フリーズした フレーム数 □ フリーズした フレームの割合

    □ 遅いレンダリングフ レーム数 □ 遅いレンダリングフ レームの割合 フレーム数 フリーズしたフ レーム数 フリーズしたフレー ムの割合(%) 遅いレンダリン グフレーム数 遅いレンダリン グフレームの割 合(%) Fragmentのレンダリング計測
  13. どの画面が遅いのかを知る - まとめ ▣ わかる数値 □ 画面単位で遅い箇所 ▣ できること □

    サンプル数と、遅いレンダリング or フリーズしたフレームの値か ら、改修優先度を決めることができる
  14. Systraceの起動 python systrace.py --time=5 -o total_systrace1.html --app=com.navitime.local.navitime sched gfx view

    wm • PCに端末を繋いで、アプリを起動 • 下記コマンドを実行 記録時間 アプリのパッケージ名 • アプリを操作
  15. 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
  16. 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 - 注目したい場所に名前を付ける
  17. Systrace - 操作 Select mode • 要素の選択 • cmd or

    dragで複数要素選択 • ダブルクリックで同じ名前の要素全選択 基本的にはこのモードだけでOK 他のモードはショートカットで代替する  (mode切替が煩わしいため)
  18. Systrace - 操作 Pan • Viewの上下左右移動 • ショートカットキー ◦ a

    → 左へ移動 ◦ d → 右へ移動 ◦ shift + a/d で大きく移動 ◦ 上下はスクロールで Zoom • ズーム • ショートカットキー ◦ w → ズームイン ◦ s → ズームアウト ◦ f → 選択した要素にフォーカスズーム
  19. NAVITIMEアプリでは 34.43% - Android Vitals → フリーズしたフレームの数値が悪い Firebase Performance →

    ルート結果画面が遅い Systrace → measureに時間がかかっている
  20. ルート結果パフォーマンス改善計測 1 3 5 8 8 (再表示) • RecyclerViewで、見えている部 分のみViewを生成

    → 経路が長くなってもパフォーマ ンス劣化なし • RecycledViewPool(ルート一覧/ 詳細でViewを共有)を使いView 生成を抑える → スワイプ切替や一覧に戻って 再表示時に速い
  21. パフォーマンス改善の取り組み - まとめ ▣ スケール別のレンダリング数値確認方法とできること □ アプリ全体の数値 ▪ Android Vitals

    ▪ リリース毎にレンダリングパフォーマンスを意識できる □ どの画面が遅いか ▪ Firebase Performance Monitoring ▪ 画面単位での改修優先度を決めることができる □ どの処理が遅いか ▪ Systrace(など) ▪ 具体的なコードの改修ができる