Save 37% off PRO during our Black Friday Sale! »

いかにしてアプリの起動時間を改善するか

45fec3b965f9269bc8d6d2cf5bd93a13?s=47 haru067
October 10, 2021

 いかにしてアプリの起動時間を改善するか

45fec3b965f9269bc8d6d2cf5bd93a13?s=128

haru067

October 10, 2021
Tweet

Transcript

  1. いかにしてアプリの 
 起動時間を改善するか 2021/10/21 DroidKaigi 2021 @haru067

  2. 自己紹介 REALITY 2021/09/30まで カンカク 2021/10/01から @haru067 アカウント しごと

  3. 起動時間 とは?

  4. ランチャーアイコンをタップ すべてのコンテンツが表示される ←この間にかかった時間→

  5. ランチャーアイコンをタップ すべてのコンテンツが表示される ←この間にかかった時間→ 何が起きているのか?

  6. アプリ起動時に起きること Android Developers - App startup time https://developer.android.com/topic/performance/vitals/launch-time 時間

  7. アプリ起動時に起きること Android Developers - App startup time https://developer.android.com/topic/performance/vitals/launch-time Application 


    onCreate 時間
  8. アプリ起動時に起きること Android Developers - App startup time https://developer.android.com/topic/performance/vitals/launch-time Application 


    onCreate Activity 
 init 時間
  9. アプリ起動時に起きること Android Developers - App startup time https://developer.android.com/topic/performance/vitals/launch-time Application 


    onCreate Activity 
 init Activity 
 onCreate in fl ate views, etc 時間
  10. アプリ起動時に起きること Android Developers - App startup time https://developer.android.com/topic/performance/vitals/launch-time Application 


    onCreate Activity 
 init その他 Activity 
 onCreate in fl ate views, etc 時間
  11. アプリ起動時に起きること Android Developers - App startup time https://developer.android.com/topic/performance/vitals/launch-time Application 


    onCreate Activity 
 init その他 Activity 
 onCreate in fl ate views, etc ただし、毎回全てが発生するわけではない 時間
  12. Android Vitalsにおける起動パターン アプリの安定性とパフォーマンスを改善するためのGoogleの取り組みとして、 
 Android Vitalsがあり、Vitalsでは3つの起動パターンを紹介している • Cold start •

    Warm start • Hot start 完全にまっさらな状態からの起動 部分的な起動(例:バックキーで戻ってからの再起動) アクティビティをフォアグラウンドにすることで起動
  13. Google Play Console上のAndroid Vitals 品質 → Android Vitals → 概要

    で確認できる
  14. • Cold start • Warm start • Hot start で、これらをどうすればいいの?

    Android Vitalsでは、次の場合に遅いとみなされる 5秒以上 2秒以上 1.5秒以上 どれをなおせばいいの? coldを想定するのが良い。なぜならcoldの中にwarmとhotも含まれているから
  15. 計測

  16. なぜ計測するのか ありがちなパターン • 「うちのアプリはXの処理に時間がかかりすぎだから直そう」 • 「Yの高速化は簡単そうだし先に直そう」

  17. なぜ計測するのか ありがちなパターン • 「うちのアプリはXの処理に時間がかかりすぎだから直そう」 • 「Yの高速化は簡単そうだし先に直そう」 ↑それってあなたの感想ですよね?

  18. なぜ直感で決めてはいけないか ボトルネックを解消しないと意味がない • 1秒かかる処理を100倍速くする vs 10秒かかる処理を2倍速くする

  19. なぜ直感で決めてはいけないか ボトルネックを解消しないと意味がない • 1秒かかる処理を100倍速くする vs 10秒かかる処理を2倍速くする 修正コストと得られる効果を考えて、作業の優先順位を決めるべき • 1日かけて処理Aを0.1秒短縮する vs

    10日で処理Bを3秒短縮する
  20. なぜ直感で決めてはいけないか ボトルネックを解消しないと意味がない • 1秒かかる処理を100倍速くする vs 10秒かかる処理を2倍速くする 修正コストと得られる効果を考えて、作業の優先順位を決めるべき • 1日かけて処理Aを0.1秒短縮する vs

    10日で処理Bを3秒短縮する そもそも、認識していないボトルネックが存在するかもしれない
  21. なぜ直感で決めてはいけないか ボトルネックを解消しないと意味がない • 1秒かかる処理を100倍速くする vs 10秒かかる処理を2倍速くする 修正コストと得られる効果を考えて、作業の優先順位を決めるべき • 1日かけて処理Aを0.1秒短縮する vs

    10日で処理Bを3秒短縮する そもそも、認識していないボトルネックが存在するかもしれない 計測より始めよ
  22. どうやって計測するか 「Releaseビルド(に相当するbuild variant)で計測する」 DebugビルドはDebug用の処理で実態に即していない結果になる可能性が高い • LeakCanary, Flipper, Hyperion, etc. 起動時間を短くする目的はユーザー体験の向上であり、開発効率の向上ではない

    (なおReleaseで計測すると、ビルドに時間がかかったりして大変)
  23. 計測手順 大きく分けて3つ • 眺める:起動処理をいくつかに分割して計測し、問題箇所の目星をつける • 特定する:怪しい箇所をコードレベルで特定して改善する • 確認する:本番環境で実際に改善されているか確認する

  24. 計測手順 大きく分けて3つ • 眺める:起動処理をいくつかに分割して計測し、問題箇所の目星をつける • 特定する:怪しい箇所をコードレベルで特定して改善する • 確認する:本番環境で実際に改善されているか確認する 
 Jetpack

    MacroBenchmark logcat CPU Pro fi ler Firebase Performance Monitoring
  25. 計測手順 大きく分けて3つ • 眺める:起動処理をいくつかに分割して計測し、問題箇所の目星をつける • 特定する:怪しい箇所をコードレベルで特定して改善する • 確認する:本番環境で実際に改善されているか確認する 
 Jetpack

    MacroBenchmark logcat CPU Pro fi ler Firebase Performance Monitoring
  26. 眺める:Jetpack MacroBenchmark (2021.10現在) alpha版、Android 10(API 29)以上 テストを実行する形で起動時間を計測することができる • cold/warm/hot start

    の計測 • 繰り返しの実行、その中央値などの計測 • テスト結果をトレースファイルとして閲覧 ここでは雰囲気程度に使い方を紹介します 
 (詳細は公式ドキュメントを参照してください)
  27. Jetpack MacroBenchmarkの使い方 専用のモジュールを作成

  28. Jetpack MacroBenchmarkの使い方 build.gradleを修正

  29. Jetpack MacroBenchmarkの使い方 com.android.library → com.android.test testImplementation, androidTestImplementation → implementation

  30. Jetpack MacroBenchmarkの使い方 AndroidManifest.xmlに追加

  31. Jetpack MacroBenchmarkの使い方 テストを記述して実行

  32. Jetpack MacroBenchmarkの使い方 テストを記述して実行

  33. 実行結果

  34. 実行結果

  35. 眺める:logcatでの計測 ActivityTaskManagerがlogcatに起動時間を出力している • I/ActivityTaskManager: Displayed com.myapp/.MainActivity: +256ms 最初の描画までにかかった時間(Displayed time) I/ActivityTaskManager:

    Fully drawn com.myapp/.MainActivity: +5s192ms Activity.reportFullyDrawn()が呼ばれるまでの時間 Activity.reportFullyDrawn()は手動で呼んであげる必要あり
  36. 図で Application 
 onCreate Activity 
 init その他 Activity 


    onCreate in fl ate views, etc Displayed time Fully Drawn
  37. 時間の計り方 処理の開始/終了の時間を計測し、その差分を印字する場合 val start = System.currentTimeMillis()

  38. 時間の計り方 処理の開始/終了の時間を計測し、その差分を印字する場合 currentTimeMillisは"時計"の値なので、ユーザーやネットワークから 
 時計が更新されたときに値が急に過去や未来に飛んでしまう val start = System.currentTimeMillis()

  39. 時間の計り方 処理の開始/終了の時間を計測し、その差分を印字する場合 currentTimeMillisは"時計"の値なので、ユーザーやネットワークから 
 時計が更新されたときに値が急に過去や未来に飛んでしまう 
 なので、SystemClock.uptimeMillisを使うと良い(もしくはSystemClock.elapsedRealTime) val start =

    System.currentTimeMillis() val start = SystemClock.uptimeMillis()
  40. 起動開始地点 どの地点をもって起動開始とするか Application.onCreate()? 


  41. 起動開始地点 どの地点をもって起動開始とするか Application.onCreate()? 
 → ContenteProvider等がApplication.onCreate()より先に実行される より厳密に計測するにはクラスの読み込み時を開始地点にしておくと良い class MyApp :

    Application() { companion object { val start = SystemClock.uptimeMillis() } ... }
  42. 計測手順 • 眺める:起動処理をいくつかに分割して計測し、問題箇所の目星をつける • 特定する:怪しい箇所をコードレベルで特定して改善する • 確認する:本番環境で実際に改善されているか確認する 
 Jetpack MacroBenchmark

    logcat CPU Pro fi ler Firebase Performance Monitoring
  43. 計測手順 • 眺める:起動処理をいくつかに分割して計測し、問題箇所の目星をつける • 特定する:怪しい箇所をコードレベルで特定して改善する • 確認する:本番環境で実際に改善されているか確認する 
 Jetpack MacroBenchmark

    logcat CPU Pro fi ler Firebase Performance Monitoring
  44. 特定する:CPU Profiler 「起動時間」を測定しないといけないのでちょっと工夫が必要 メニューから 
 「Run」 → 「Edit Con fi

    gurations」を選択
  45. CPU Profilerの使い方 Pro fi lingタブの「Start this recording on startup」にチェックし、 


    「CPU activity」「Sample Java Methods」を選択
  46. CPU Profilerの使い方 Pro fi leボタン(or Run → Pro fi le

    'app')で実行 Pro fi leタブにプロファイルの様子が表示されるので、起動が完了したら止める
  47. CPU Profilerを活用する スレッド毎にトレースが表示される

  48. コードから実行する場合 Debug.startMethodTracingSampling()及び Debug.stopMethodTracing() で 
 コードから動的にプロファイルすることできる。次のようなケースで便利 • 計測を自動化したい • 特定の開始/終了地点を正確に記録したい

    実行結果はstartで指定したパスに保存されるので、それをPro fi lerで開けばよい
  49. CPU Profilerを活用する 各スレッドを開くと時系列に沿った各メソッドの実行時間が確認できる

  50. CPU Profilerを活用する 各スレッドを開くと時系列に沿った各メソッドの実行時間が確認できる 例として、MainActivity.onCreateを調査してみましょう

  51. 例:MainActivity.onCreateの調査 MainActivity.onCreateをクリック

  52. 例:MainActivity.onCreateの調査 右のパネルに色々表示される。今回はFlame Chartをクリック

  53. Flame Chart

  54. Flame Chart 横軸を実行時間の割合(%)として、コールスタックが縦に積まれる 左から実行時間の長い順に並ぶ

  55. Flame Chart 横軸を実行時間の割合(%)として、コールスタックが縦に積まれる ボトルネック? 問題なし? 左から実行時間の長い順に並ぶ

  56. Flame Chart

  57. Flame Chart

  58. Flame Chart setContentView() badFunction()

  59. Flame Chartの使用例 setContentViewに一番時間がかかっている(でも直すのは無理そう)

  60. Flame Chartの使用例 badFunctionに時間がかかっている。内訳はrepeat()とLog.d()

  61. 修正作業 ぶっちゃけアプリによりけりなのでなんとも言えないが、 
 大方針としてはだいたい次の3つに集約されるのでは • キャッシュ • 並列化・並行化 • 不要な処理の削除(遅延させる)

  62. 計測手順 • 眺める:起動処理をいくつかに分割して計測し、問題箇所の目星をつける • 特定する:怪しい箇所をコードレベルで特定して改善する • 確認する:本番環境で実際に改善されているか確認する 
 Jetpack MacroBenchmark

    logcat CPU Pro fi ler Firebase Performance Monitoring
  63. 計測手順 • 眺める:起動処理をいくつかに分割して計測し、問題箇所の目星をつける • 特定する:怪しい箇所をコードレベルで特定して改善する • 確認する:本番環境で実際に改善されているか確認する 
 Jetpack MacroBenchmark

    logcat CPU Pro fi ler Firebase Performance Monitoring
  64. 確認する 手元で計測して早くなったらいいのか?

  65. 確認する 手元で計測して早くなったらいいのか? そんなことはない

  66. 手元では再現しないパターン • APIリクエストが本番環境のデータだと遅くなる • 特定地域・特定回線で遅くなる • 特定デバイスで遅くなる • 普段は早いが、極端に遅いことが(特定条件化で)たま〜にある •

    ユーザーは常に遅いシステム以上に、普段速くてたまに極端に遅くなるシステ ムを嫌う。意外と危険な兆候
  67. Firebase Performance Monitoring 先程述べたようなことを 
 大体カバーしているので便利

  68. めでたし? 無事早くなりました! が、アプリの運用を考えると 
 「遅くしないこと」も頑張らないといけない

  69. 運用・監視

  70. なぜ監視が必要か? 機能追加を繰り返す過程で起動時間は知らない間に遅くなる 「なんか最近起動重くない?」と気づくのはまだマシなほうで、じわじわと起動時間 が長くなった場合はそれが当たり前になって誰も気づかない可能性がある

  71. よくある過ち 「うちはちゃんと起動時間のログをとっているから問題ないよ」

  72. よくある過ち 「うちはちゃんと起動時間のログをとっているから問題ないよ」 → ログを活用する習慣がないなら、それはログをとっていないのと同じ

  73. 監視するには フローとして組み込む • リリース時の確認項目や定例など 目につくところに流す • アラートを鳴らすとか、Slackに流すとか • XXXにアクセスすれば見れるよ、は大体誰も見ない わかりやすく流す

    • 情報量が多いとやはり誰も見なくなる(どこを見ていいのかわからなくなる)
  74. 某アプリでの事例 • FirebaseのログをData Studio(BIツール)で可視化※ • (週1リリースなので)週次の定例でグラフを確認 ※ Firebaseのログは一度Big Queryにエクスポートし、それをDataStudio上で参照

  75. 効果あった?

  76. 効果あった? あった

  77. 効果あった? あった 最高!

  78. まとめ • 3種の起動パターン: cold/warm/hot start、まずはcoldから改善していくの がオススメ • 計測より始めよ • 3つの計測:

    大雑把に怪しい箇所を見つけ、詳細を調べ、実際のユーザーにおける 起動時間が改善しているか確認する • 計測ツール: MacroBenchmark, logcat, CPU Pro fi ler, Firebase Performance Monitoring • 速くするだけでなく、「遅くしない」ための監視も頑張ろう
  79. 参考 https://developer.android.com/topic/performance/vitals/launch-time https://dev.to/pyricau/android-vitals-what-time-is-it-2oih https://dropbox.tech/mobile/how-we-sped-up-dropbox-android-app-startup-by-30- https://medium.com/okcredit/how-okcredit-android-app-improved-cold-startup- by-70-e02bda4836a8 https://developer.android.com/studio/pro fi le/generate-trace-logs https://developer.android.com/studio/pro

    fi le/measuring-performance https://medium.com/androiddevelopers/app-startup-part-1-34f57b65cacd https://developer.android.com/studio/pro fi le/cpu-pro fi ler