every Tech Blog

株式会社エブリーのTech Blogです。

デリッシュキッチンの Android アプリの起動時間を半分にした話

はじめに

こんにちは、デリッシュキッチンでクライアントエンジニアを担当している kikuchi です。

デリッシュキッチンの Android アプリ開発チームでは新規機能の開発だけでなく、日々アプリの改善のために不具合の修正や挙動の改善についても力を入れており、アプリのパフォーマンスの部分など細かい部分も数値が改善されているかシビアに計測データで確認しています。
今回はそのパフォーマンスの部分で 「アプリの起動時間の改善」 の観点で特に効果が高かった対応をまとめてみたいと思います。

アプリの起動時間を確認する方法

アプリ起動時間は以下で調べることが出来ます。

  1. Google Play Console の Android Vitals でコールドスタートなどの時間を確認する
  2. アプリの TTID (Time To Initial Display) と TTFD (Time To Full Display) を確認する
  3. ログを仕込む

1 については、Google Play Console 上で Android Vitals という機能を使ってコールドスタートなどの数値を確認する、というものになります。
こちらは OS のバージョン、CPU、RAM などで細かくフィルタリングができ、また何より類似するサービスのアプリや、任意で選択したアプリと比較できるという点が他社のアプリと直接比較できる数少ない指標になるため重宝しています。
(なお、実値で比較できるわけではなく、あくまでコールドスタートなどの時間が長いユーザの割合が比較できるのみ)

2 については Google Developers の公式サイトで詳しく紹介されています。

TTID についてはどのアプリについても Logcat で簡単に確認することが出来ます。
以下は API Level 36 のエミュレータで Chrome アプリを起動した場合のログになります。

Displayed でフィルタをかけて表示したログで、「1s312ms」という数値が TTID となります。
上記のようにアプリを起動するだけで出力されるログのため、自社で開発したアプリ、ストアからインストールしたアプリなど関係なく確認することが出来ます。

なお、TTFD については ComponentActivity の reportFullyDrawn() というメソッドを意図的に呼ぶ必要があるため、基本は自社で開発したアプリのみで確認できます。
TTFD については今回まとめる内容と深く関わる部分ではないため、割愛させていただきます。

3 については、細かく Log クラスなどを駆使してログを仕込んでいく方法になります。
こちらはあくまでアプリが起動してから (つまりは Application を継承したクラスの onCreate メソッドがコールされてから) の処理にログを仕込めるものとなるため、怪しい実装が無いかを特定する際に最適な方法だと言えます。

調査観点

起動時間を改善する、と一言で言っても、手当たり次第調査をしても効果は出にくいと考えたため、以下に観点を絞って調査をしました。

  1. アプリ起動時に不要な処理を実行していないか
  2. 不要なライブラリが存在しないか
  3. 起動時間そのものを最適化して短縮できないか

結果を先にお伝えすると、1 についてはログを細かく仕込んで調査したものの全く問題なく、余計な処理や大きく短縮できそうな処理は存在せず、2 と 3 に大きく効果がある改善ポイントが存在していました。

以降の項目で 2 と 3 について細かくまとめていきたいと思います。

不要ライブラリの調査

不要ライブラリが存在するかについては、導入しているライブラリを一つ一つ確認していきました。
とはいえ、デリッシュキッチンのプログラムの規模はかなり大きく全部で 100 個近くのライブラリを導入しているため、AndroidX など必須のライブラリは除いて画像関連のライブラリなどサードパーティ製のライブラリに絞って調査することにしました。

結果、Firebase In-App Messaging がかなり起動時間に悪影響を与えていたということが分かりました。
複数端末で Firebase In-App Messaging を除外した状態で TTID を検証した結果は以下のようになりました。

  • デバイス A / OS 8.1.0 / RAM 4GB … 180msec 程度改善
  • デバイス B / OS 12 / RAM 6GB … 120msec 程度改善
  • デバイス C / OS 13 / RAM 8GB … 30msec 程度改善

OS が古い、RAM が少ない端末ほど改善幅が大きい、という結果になりました。
こちらについてはデリッシュキッチンチーム全体で確認した結果、今は使用しておらず今後も使う可能性は無い、ということが分かったためライブラリを除外する結論となりました。

何故 Firebase In-App Messaging が起動時間に悪影響を与えていたのか

In-App Messaging ですが、どこでもメッセージを表示できるという特性上、WebView で作られていたことが起動時間を長くしていた要因でした。
WebView の初期化、生成は非常にコストがかかるうえ、Firebase In-App Messaging はライブラリを導入しているだけで問答無用でアプリ起動時に初期化される仕組みとなっており、意図的にプログラムで初期化処理を呼ばなくても初期化処理が走っていました。
結果、アプリ起動時に WebView の初期化処理まで走って余計に時間がかかっているという状況に繋がっていました。

他にも In-App Messaging の機能を提供しているライブラリでも同様に初期化処理に時間がかかっていたため、In-App Messaging のライブラリを使用している場合はライブラリの見直しをしてみるのも良いかも知れません。

起動時間そのものを最適化する方法の調査

起動時間そのものを最適化する方法については、Android Gradle Plugin (AGP) と Android Jetpack の一部として提供されている Baseline Profiles という仕組みが導入コストが低く、改善の期待値が高いということが分かりました。

Baseline Profiles については Google Developers に概要がまとめられています。
https://developer.android.com/topic/performance/baselineprofiles/overview?hl=ja

Baseline Profiles とは、アプリの起動に必要な情報を事前 (AOT) コンパイルしておくことで、アプリの起動時に実行するコードパスの解釈、ジャストインタイム (JIT) コンパイルを省略でき、初回起動からのコード実行速度が約 30% も向上するものとなります。
なお、Baseline Profiles を採用することで起動時間を短縮するだけでなく、ジャンク (かくつき) の軽減、全体的なパフォーマンスの向上にも繋がります。

Baseline Profiles の導入自体は容易です。次の項目で導入手順をまとめます。

Baseline Profiles の導入手順

実際にプロジェクトに追加する手順を画像を交えて紹介します。

①Android Studio で「File > New > New Module」を選択

②Templates で「Baseline Profile Generator」を選択し、必要に応じて値を設定し、Finish を選択
※「Use Gradle Managed Device」にチェックを付けることで、Gradle で管理されているデバイス (GMD) を使用して Baseline Profiles を作成するための初期設定が自動的に行われます

③ツールバーの Run / Debug Configrations で、手順 2 にて生成した項目を選択して Run を実行

④app モジュール > src > release / generated / baselineProfiles に baseline-prof.txt が出力される

以上で完了となります。
※実際のプロジェクトの環境によっては手順が異なる可能性があるため、先述した公式サイトの情報を確認しながら設定を行うことをおすすめします。

Baseline Profiles の導入結果

Baseline Profiles を導入した結果の TTID は以下のようになりました。

  • デバイス A / OS 8.1.0 / RAM 4GB … 200msec 程度改善
  • デバイス B / OS 12 / RAM 6GB … 150msec 程度改善
  • デバイス C / OS 13 / RAM 8GB … 70msec 程度改善

こちらも不要ライブラリ除外と同様、OS が古い、RAM が少ない端末ほど改善幅が大きい、という結果になりました。
Baseline Profiles についてはプロジェクトの規模などにも大きく左右されますが、大きな改善が見込めることが分かります。

対応した結果

今回注力して対応した「不要ライブラリ除外」、「Baseline Profiles の導入」のみで、最大で 400msec 近くもの起動時間を短縮に成功しました。
調査や対応に当然工数はかかっているものの、検証込みで 1 週間程度の少ない工数の見返りとしてはかなり大きいものだったと感じています。

これ以外にも TTID 以外の指標を使用して細かい計測やソースのリファクタリングなどを経て、最終的には対応前と対応後で起動時間を半分にすることが出来ました。

なお、起動時間を短縮した結果、一番恩恵を受けたのがスプラッシュ画面になりました。
OS 12 以降はスプラッシュ画面 (Splash Screen) が表示されるようになりましたが、TTID が短いほどスプラッシュ画面の表示が短くなります。
スプラッシュ画面はアイコンだけの画面になっていて無機質な画面に見えることが多く、スプラッシュ画面の表示時間が長いとフリーズしているように見えるため、すぐにユーザが操作できる状態になることはユーザビリティの向上に繋がります。

おわりに

Firebase In-App Messaging の除外、Baseline Profiles 導入といった工数が少ない対応で大幅な起動時間の短縮に繋げることが出来ました。
デリッシュキッチンのようにライブラリの見直しだけでも起動時間の改善に繋がることもあるので、バージョン更新だけでなく、使用有無の棚卸しをするだけでも非常に効果的だと考えます。
また Baseline Profiles についても、こういったことをやりたい、と漠然と気になったことでも調べてみると機能が提供されていることがあるため、エンジニアの原点ではありますが、気になったら調べる、という行動は必要不可欠だと考えます。

本記事の情報が皆様のお役に立てれば幸いです。