1. はじめに
最終更新日: 2021 年 3 月 11 日
ビューのパフォーマンスを測定する必要があるのはなぜですか?
ビューは Android アプリの要であり、ユーザー エクスペリエンスに直接影響を与えます。たとえば、アクティビティやフラグメントには、ユーザーが操作する View コンポーネントを保持する UI が含まれています。画面に完全に描画されるまで、UI のコンテンツ全体を表示できません。画面の動作が遅かったり、フリーズしたりすると、アプリのユーザー操作が直接妨げられ、ユーザー エクスペリエンスが低下します。
Firebase Performance Monitoring では、これらのパフォーマンス指標をすぐに利用できるのではないですか?
Firebase Performance Monitoring では、アプリの起動時間(つまり、最初のアクティビティの読み込み時間のみ)や画面レンダリングのパフォーマンス(つまり、アクティビティの遅延フレームとフリーズ フレーム。フラグメントは対象外)など、一部のパフォーマンス データが自動的にキャプチャされます。ただし、業界アプリには通常、多くのアクティビティではなく、1 つのアクティビティと複数のフラグメントがあります。また、多くのアプリでは、より複雑なユースケースに対応するために独自のカスタムビューを実装しています。そのため、アプリでカスタムコード トレースを設定して、アクティビティとフラグメントの両方の読み込み時間と画面レンダリングのパフォーマンスを測定する方法を理解しておくと便利です。この Codelab を簡単に拡張して、カスタムビュー コンポーネントのパフォーマンスを測定することもできます。
学習内容
- Firebase Performance Monitoring を Android アプリに追加する方法
- アクティビティまたはフラグメントの読み込みを理解する
- カスタム コード トレースをインストルメント化して、アクティビティまたはフラグメントの読み込み時間を測定する方法
- 画面レンダリングと低速/フリーズしたフレームについて
- 指標を使用してカスタムコード トレースを計測し、画面の遅延やフリーズを記録する方法
- Firebase コンソールで収集された指標を表示する方法
必要なもの
- Android Studio 4.0 以降
- Android デバイス/エミュレータ
- Java バージョン 8 以降
2. 設定方法
コードを取得する
次のコマンドを実行して、この Codelab のサンプルコードのクローンを作成します。これにより、マシンに codelab-measure-android-view-performance
というフォルダが作成されます。
$ git clone https://github.com/FirebaseExtended/codelab-measure-android-view-performance.git
$ cd codelab-measure-android-view-performance
マシンに git がない場合は、GitHub から直接コードをダウンロードすることもできます。
measure-view-performance-start
プロジェクトを Android Studio にインポートします。コンパイル エラーが表示されるか、google-services.json
ファイルがないという警告が表示されることがあります。この点は、このステップの次のセクションで修正します。
この Codelab では、Firebase Assistant プラグインを使用して、Android アプリを Firebase プロジェクトに登録し、Android プロジェクトに必要な Firebase 構成ファイル、プラグイン、依存関係を追加します。これらの操作はすべて Android Studio から行えます。
アプリを Firebase に接続する
- [Android Studio]/[Help] > [Check for updates] に移動して、Android Studio と Firebase Assistant の最新版を使用していることを確認します。
- [Tools] > [Firebase] を選択して、[Assistant] ペインを開きます。
- アプリに追加する Performance Monitoring を選択し、[Performance Monitoring を使ってみる] をクリックします。
- ボタンをクリックして新しいプロジェクトを作成し、プロジェクト名(例:
Measure Performance Codelab
)を入力します。 - [続行] をクリックします。
- Firebase の利用規約が表示されたら、内容を読み、同意して [続行] をクリックします。
- (省略可)Firebase コンソールで AI アシスタンス(「Gemini in Firebase」)を有効にします。
- この Codelab では Google アナリティクスは必要ないため、Google アナリティクスのオプションをオフに切り替えます。
- 次に、新しい Firebase アプリを Android Studio プロジェクトに接続するためのダイアログが表示されます。
- Android Studio の [アシスタント] ペインに、アプリが Firebase に接続されたことを確認するメッセージが表示されます。
Performance Monitoring をアプリに追加する
Android Studio の [Assistant] ペインで、[Add Performance Monitoring to your app] をクリックします。
[変更を承認] ダイアログが表示され、Android Studio がアプリを同期して、必要な依存関係がすべて追加されていることを確認します。
最後に、Android Studio の [アシスタント] ペインに、すべての依存関係が正しく設定されたことを示す成功メッセージが表示されます。
追加の手順として、手順「(省略可)デバッグ ロギングを有効にする」の手順に沿って、デバッグ ロギングを有効にします。同じ手順は、一般公開ドキュメントにも記載されています。
3. アプリを実行する
アプリと Performance Monitoring SDK の統合が正常に完了すると、プロジェクトがコンパイルされるようになります。Android Studio で、[Run] > [Run 'app'] をクリックして、接続された Android デバイス/エミュレータでアプリをビルドして実行します。
アプリには、対応するアクティビティとフラグメントに移動する 2 つのボタンがあります。
この Codelab の次のステップでは、アクティビティまたはフラグメントの読み込み時間と画面レンダリングのパフォーマンスを測定する方法を学びます。
4. アクティビティまたはフラグメントの読み込みを理解する
このステップでは、アクティビティまたはフラグメントの読み込み中にシステムが何を行っているかを学びます。
Activity の読み込みを理解する
アクティビティの場合、読み込み時間は、アクティビティ オブジェクトが作成されてから、最初のフレームが画面に完全に描画されるまでの時間として定義されます(この時点で、ユーザーはアクティビティの完全な UI を初めて目にします)。アプリが完全に描画されたかどうかを測定するには、reportFullyDrawn()
メソッドを使用して、アプリを起動してからすべてのリソースとビュー階層が完全に表示されるまでの経過時間を測定します。
大まかには、アプリが startActivity(Intent)
を呼び出すと、システムは次のプロセスを自動的に実行します。各プロセスには完了するまでに時間がかかるため、アクティビティの作成からユーザーが画面上でアクティビティの UI を確認するまでの時間が長くなります。
フラグメントの読み込みを理解する
アクティビティと同様に、フラグメントの読み込み時間は、フラグメントがホスト アクティビティにアタッチされてから、フラグメント ビューの最初のフレームが画面に完全に描画されるまでの時間として定義されます。
5. Activity の読み込み時間を測定する
最初のフレームの遅延はユーザー エクスペリエンスの低下につながるため、ユーザーがどの程度の初期読み込み遅延を経験しているかを把握することが重要です。この読み込み時間を測定するには、カスタムコード トレースを設定します。
- Activity オブジェクトが作成されたらすぐに、Activity クラスでカスタムコード トレース(
TestActivity-LoadTime
という名前)を開始します。
TestActivity.java
public class TestActivity extends AppCompatActivity {
// TODO (1): Start trace recording as soon as the Activity object is created.
private final Trace viewLoadTrace = FirebasePerformance.startTrace("TestActivity-LoadTime");
// ...
}
onCreate()
コールバックをオーバーライドし、setContentView()
メソッドで追加された View を取得します。
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Current Activity's main View (as defined in the layout xml file) is inflated after this
setContentView(R.layout.activity_test);
// ...
// TODO (2): Get the View added by Activity's setContentView() method.
View mainView = findViewById(android.R.id.content);
// ...
}
FistDrawListener
の実装が含まれています。これにはonDrawingStart()
とonDrawingFinish()
の 2 つのコールバックがあります。(FirstDrawListener
とそのパフォーマンスに影響を与える可能性のあるものについて詳しくは、次のセクションをご覧ください)アクティビティのonCreate()
コールバックの最後にFirstDrawListener
を登録します。onDrawingFinish()
コールバックでviewLoadTrace
を停止する必要があります。
TestActivity.java
// TODO (3): Register the callback to listen for first frame rendering (see
// "OnFirstDrawCallback" in FirstDrawListener) and stop the trace when View drawing is
// finished.
FirstDrawListener.registerFirstDrawListener(mainView, new FirstDrawListener.OnFirstDrawCallback() {
@Override
public void onDrawingStart() {
// In practice you can also record this event separately
}
@Override
public void onDrawingFinish() {
// This is when the Activity UI is completely drawn on the screen
viewLoadTrace.stop();
}
});
- アプリを再実行し、logcat を「Logging trace metric」でフィルタします。
LOAD ACTIVITY
ボタンをタップし、次のようなログを探します。
I/FirebasePerformance: Logging trace metric: TestActivity-LoadTime (duration: XXXms)
🎉 おめでとうございます。アクティビティの読み込み時間を測定し、そのデータを Firebase Performance Monitoring にレポートしました。この Codelab の後半で、記録された指標を Firebase コンソールで確認します。
FirstDrawListener の目的
直前のセクションでは、FirstDrawListener
を登録しました。FirstDrawListener
の目的は、最初のフレームの描画が開始されてから終了するまでの時間を測定することです。
ViewTreeObserver.OnDrawListener
を実装し、ビューツリーが描画される直前に呼び出される onDraw()
コールバックをオーバーライドします。次に、結果をラップして、2 つのユーティリティ コールバック onDrawingStart()
と onDrawingFinish()
を提供します。
FirstDrawListener
の完全なコードは、この Codelab のソースコードにあります。
6. フラグメントの読み込み時間を測定する
フラグメントの読み込み時間の測定は、アクティビティの測定と似ていますが、いくつかの違いがあります。ここでも、カスタムコード トレースをインストルメント化します。
onAttach()
コールバックをオーバーライドして、fragmentLoadTrace
の録画を開始します。このトレースにTest-Fragment-LoadTime
という名前を付けます。
前の手順で説明したように、Fragment オブジェクトはいつでも作成できますが、アクティブになるのはホスト アクティビティにアタッチされたときだけです。
TestFragment.java
public class TestFragment extends Fragment {
// TODO (1): Declare the Trace variable.
private Trace fragmentLoadTrace;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
// TODO (2): Start trace recording as soon as the Fragment is attached to its host Activity.
fragmentLoadTrace = FirebasePerformance.startTrace("TestFragment-LoadTime");
}
onViewCreated()
コールバックでFirstDrawListener
を登録します。次に、アクティビティの例と同様に、onDrawingFinish()
でトレースを停止します。
TestFragment.java
@Override
public void onViewCreated(@NonNull View mainView, Bundle savedInstanceState) {
super.onViewCreated(mainView, savedInstanceState);
// ...
// TODO (3): Register the callback to listen for first frame rendering (see
// "OnFirstDrawCallback" in FirstDrawListener) and stop the trace when view drawing is
// finished.
FirstDrawListener.registerFirstDrawListener(mainView, new FirstDrawListener.OnFirstDrawCallback() {
@Override
public void onDrawingStart() {
// In practice you can also record this event separately
}
@Override
public void onDrawingFinish() {
// This is when the Fragment UI is completely drawn on the screen
fragmentLoadTrace.stop();
}
});
- アプリを再実行し、logcat を「Logging trace metric」でフィルタします。
LOAD FRAGMENT
ボタンをタップし、次のようなログを探します。
I/FirebasePerformance: Logging trace metric: TestFragment-LoadTime (duration: XXXms)
🎉 おめでとうございます。フラグメントの読み込み時間を測定し、そのデータを Firebase Performance Monitoring にレポートしました。この Codelab の後半で、記録された指標を Firebase コンソールで確認します。
7. 画面レンダリングと、遅いフレーム/フリーズしたフレームについて
UI レンダリングとは、アプリからフレームを生成して画面に表示することです。ユーザーのアプリ操作をスムーズにするためには、16 ms 以内にフレームをレンダリングし、60 フレーム/秒を達成する必要があります(Why 60fps? の動画をご覧ください)。アプリの UI レンダリングが遅い場合、システムはフレームをスキップせざるを得ず、ユーザーはアプリの動作がスムーズでないと感じます。これをジャンクと呼びます。
同様に、フリーズしたフレームは、レンダリングに 700 ミリ秒より長くかかる UI フレームです。フレームがレンダリングされている間、アプリが停止しているように見え、ユーザー入力にほぼ 1 秒間反応しないため、この遅延は問題です。
8. フラグメントの低速/フリーズしたフレームを測定します。
Firebase Performance Monitoring は、アクティビティの遅延フレームとフリーズ フレームを自動的にキャプチャします(ただし、ハードウェア アクセラレーションが有効になっている場合に限ります)。ただし、この機能は現在、フラグメントではご利用いただけません。フラグメントの遅いフレーム/フリーズしたフレームは、フラグメントのライフサイクルにおける onFragmentAttached()
コールバックと onFragmentDetached()
コールバックの間のアクティビティ全体の遅いフレーム/フリーズしたフレームとして定義されます。
AppStateMonitor
クラス(Activity の画面トレースの記録を担当する Performance Monitoring SDK の一部)から着想を得て、ScreenTrace
クラス(この Codelab のソースコード リポジトリの一部)を実装しました。ScreenTrace
クラスは、アクティビティの FragmentManager
のライフサイクル コールバックに接続して、低速フレームやフリーズ フレームをキャプチャできます。このクラスは、次の 2 つの公開 API を提供します。
recordScreenTrace()
: 画面トレースの記録を開始しますsendScreenTrace()
: 画面トレースの記録を停止し、合計、低速、フリーズしたフレーム数のログにカスタム指標を付加します。
これらのカスタム指標を関連付けることで、フラグメントの画面トレースをアクティビティの画面トレースと同じように処理し、Firebase コンソールの [パフォーマンス] ダッシュボードで他の画面レンダリング トレースとともに表示できます。
Fragment の画面トレースを記録する方法は次のとおりです。
- フラグメントをホストするアクティビティで
ScreenTrace
クラスを初期化します。
MainActivity.java
// Declare the Fragment tag
private static final String FRAGMENT_TAG = TestFragment.class.getSimpleName();
// TODO (1): Declare the ScreenTrace variable.
private ScreenTrace screenTrace;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// TODO (2): Initialize the ScreenTrace variable.
screenTrace = new ScreenTrace(this, FRAGMENT_TAG);
// ...
}
- フラグメントを読み込むときに、
FragmentLifecycleCallbacks
を登録し、onFragmentAttached()
コールバックとonFragmentDetached()
コールバックをオーバーライドします。この作業は Google が代行します。onFragmentAttached()
コールバックで画面トレースの録画を開始し、onFragmentDetached()
コールバックで録画を停止する必要があります。
MainActivity.java
private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
new FragmentManager.FragmentLifecycleCallbacks() {
@Override
public void onFragmentAttached(@NonNull FragmentManager fm, @NonNull Fragment f, @NonNull Context context) {
super.onFragmentAttached(fm, f, context);
// TODO (3): Start recording the screen traces as soon as the Fragment is
// attached to its host Activity.
if (FRAGMENT_TAG.equals(f.getTag()) && screenTrace.isScreenTraceSupported()) {
screenTrace.recordScreenTrace();
}
}
@Override
public void onFragmentDetached(@NonNull FragmentManager fm, @NonNull Fragment f) {
super.onFragmentDetached(fm, f);
// TODO (4): Stop recording the screen traces as soon as the Fragment is
// detached from its host Activity.
if (FRAGMENT_TAG.equals(f.getTag()) && screenTrace.isScreenTraceSupported()) {
screenTrace.sendScreenTrace();
}
// Unregister Fragment lifecycle callbacks after the Fragment is detached
fm.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallbacks);
}
};
- アプリを再実行し、
LOAD FRAGMENT
ボタンをタップします。数秒待ってから、下部のナビゲーション バーにあるback button
をクリックします。
「ロギング トレース指標」で logcat をフィルタし、次のようなログを探します。
I/FirebasePerformance: Logging trace metric: _st_MainActivity-TestFragment (duration: XXXms)
「FireperfViews」で logcat をフィルタし、次のようなログを探します。
D/FireperfViews: sendScreenTrace MainActivity-TestFragment, name: _st_MainActivity-TestFragment, total_frames: XX, slow_frames: XX, frozen_frames: XX
🎉 おめでとうございます。フラグメントの低速/フリーズ フレームを測定し、そのデータを Firebase Performance Monitoring にレポートしました。この Codelab の後半で、Firebase コンソールで記録された指標を確認します。
9. Firebase コンソールで指標を確認する
- logcat で Firebase コンソールの URL をクリックして、トレースの詳細ページに移動します。
または、Firebase コンソールで、アプリを含むプロジェクトを選択します。左側のパネルで [リリースとモニタリング] セクションを見つけて、[パフォーマンス] をクリックします。
- メインの [ダッシュボード] タブで、トレース テーブルまで下にスクロールし、[カスタム トレース] タブをクリックします。この表には、先ほど追加したカスタムコード トレースと、
_app_start
トレースなどのすぐに使用できるトレースが表示されます。 - 2 つのカスタムコード トレース
TestActivity-LoadTime
とTestFragment-LoadTime
を見つけます。いずれかの [期間] をクリックすると、収集されたデータの詳細が表示されます。
- カスタムコード トレースの詳細ページには、トレースの期間(測定された読み込み時間)に関する情報が表示されます。
- カスタム画面トレースのパフォーマンス データも確認できます。
- [ダッシュボード] のメインタブに戻り、トレース テーブルまで下にスクロールして、[画面レンダリング] タブをクリックします。この表には、先ほど追加したカスタム画面トレースと、
MainActivity
トレースなどの組み込みの画面トレースが表示されます。 - カスタム画面トレース
MainActivity-TestFragment
を探します。トレース名をクリックすると、レンダリングが遅いフレームとフリーズしたフレームの集計データが表示されます。
10. 完了
これで完了です。Firebase Performance Monitoring を使用して、アクティビティとフラグメントの読み込み時間と画面レンダリングのパフォーマンスを測定できました。
達成したこと
- Firebase Performance Monitoring をサンプルアプリに統合した
- これで、View の読み込みのライフサイクルを理解できました。
- カスタムコード トレースを追加して、アクティビティとフラグメントの両方の読み込み時間を測定した
- カスタム指標を含むカスタム画面トレースを追加して、遅いフレームやフリーズしたフレームを記録した
次のステップ
Firebase Performance では、カスタム トレース以外にもアプリのパフォーマンスを測定する方法が用意されています。アプリ起動時間、フォアグラウンド アプリ、バックグラウンド アプリのパフォーマンス データを自動的に測定します。Firebase コンソールでこれらの指標を確認します。
また、Firebase Performance は自動 HTTP/S ネットワーク リクエスト モニタリングも提供します。これにより、コードを 1 行も記述することなく、ネットワーク リクエストを簡単に計測できます。アプリからネットワーク リクエストを送信して、Firebase コンソールで指標を確認していただけますか?
参考
カスタムコード トレースを使用して Activity/Fragment の読み込み時間と画面レンダリングのパフォーマンスを測定する方法を理解したところで、オープンソースのコードベースを調べて、アプリの一部である任意の Activity/Fragment の指標をすぐに取得できるかどうかを確認してみましょう。ご希望でしたら、お気軽に PR をお送りください :-)
11. ボーナス学習
アクティビティの読み込み中に何が起こっているかを把握すると、アプリのパフォーマンス特性をより深く理解できます。前のステップでは、アクティビティの読み込み中に何が起こるかを大まかに説明しましたが、次の図では各フェーズをより詳細に説明しています。