Firebase Performance Monitoring を使用して読み込み時間と画面レンダリングを測定する

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 に接続する

  1. [Android Studio]/[Help] > [Check for updates] に移動して、Android Studio と Firebase Assistant の最新版を使用していることを確認します。
  2. [Tools] > [Firebase] を選択して、[Assistant] ペインを開きます。
    e791bed0999db1e0.png
  3. アプリに追加する Performance Monitoring を選択し、[Performance Monitoring を使ってみる] をクリックします。
  4. ボタンをクリックして新しいプロジェクトを作成し、プロジェクト名(例: Measure Performance Codelab)を入力します。
  5. [続行] をクリックします。
  6. Firebase の利用規約が表示されたら、内容を読み、同意して [続行] をクリックします。
  7. (省略可)Firebase コンソールで AI アシスタンス(「Gemini in Firebase」)を有効にします。
  8. この Codelab では Google アナリティクスは必要ないため、Google アナリティクスのオプションをオフに切り替えます
  9. 次に、新しい Firebase アプリを Android Studio プロジェクトに接続するためのダイアログが表示されます。
    42c498d28ead2b77.png
  10. Android Studio の [アシスタント] ペインに、アプリが Firebase に接続されたことを確認するメッセージが表示されます。
    dda8bdd9488167a0.png

Performance Monitoring をアプリに追加する

Android Studio の [Assistant] ペインで、[Add Performance Monitoring to your app] をクリックします。

[変更を承認] ダイアログが表示され、Android Studio がアプリを同期して、必要な依存関係がすべて追加されていることを確認します。

9b58145acc4be030.png

最後に、Android Studio の [アシスタント] ペインに、すべての依存関係が正しく設定されたことを示す成功メッセージが表示されます。

aa0d46fc944e0c0b.png

追加の手順として、手順「(省略可)デバッグ ロギングを有効にする」の手順に沿って、デバッグ ロギングを有効にします。同じ手順は、一般公開ドキュメントにも記載されています。

3. アプリを実行する

アプリと Performance Monitoring SDK の統合が正常に完了すると、プロジェクトがコンパイルされるようになります。Android Studio で、[Run] > [Run 'app'] をクリックして、接続された Android デバイス/エミュレータでアプリをビルドして実行します。

アプリには、対応するアクティビティとフラグメントに移動する 2 つのボタンがあります。

410d8686b4f45c33.png

この Codelab の次のステップでは、アクティビティまたはフラグメントの読み込み時間と画面レンダリングのパフォーマンスを測定する方法を学びます。

4. アクティビティまたはフラグメントの読み込みを理解する

このステップでは、アクティビティまたはフラグメントの読み込み中にシステムが何を行っているかを学びます。

Activity の読み込みを理解する

アクティビティの場合、読み込み時間は、アクティビティ オブジェクトが作成されてから、最初のフレームが画面に完全に描画されるまでの時間として定義されます(この時点で、ユーザーはアクティビティの完全な UI を初めて目にします)。アプリが完全に描画されたかどうかを測定するには、reportFullyDrawn() メソッドを使用して、アプリを起動してからすべてのリソースとビュー階層が完全に表示されるまでの経過時間を測定します。

大まかには、アプリが startActivity(Intent) を呼び出すと、システムは次のプロセスを自動的に実行します。各プロセスには完了するまでに時間がかかるため、アクティビティの作成からユーザーが画面上でアクティビティの UI を確認するまでの時間が長くなります。

c20d14b151549937.png

フラグメントの読み込みを理解する

アクティビティと同様に、フラグメントの読み込み時間は、フラグメントがホスト アクティビティにアタッチされてから、フラグメント ビューの最初のフレームが画面に完全に描画されるまでの時間として定義されます。

5. Activity の読み込み時間を測定する

最初のフレームの遅延はユーザー エクスペリエンスの低下につながるため、ユーザーがどの程度の初期読み込み遅延を経験しているかを把握することが重要です。この読み込み時間を測定するには、カスタムコード トレースを設定します。

  1. 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");

    // ...

}
  1. 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);     

    // ...
}
  1. 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();             
        }         
    });
  1. アプリを再実行し、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. フラグメントの読み込み時間を測定する

フラグメントの読み込み時間の測定は、アクティビティの測定と似ていますが、いくつかの違いがあります。ここでも、カスタムコード トレースをインストルメント化します。

  1. 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");
   }
  1. 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();
       }
   });
  1. アプリを再実行し、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 の画面トレースを記録する方法は次のとおりです。

  1. フラグメントをホストするアクティビティで 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);

    // ...
}
  1. フラグメントを読み込むときに、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);
           }
       };
  1. アプリを再実行し、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 コンソールで指標を確認する

  1. logcat で Firebase コンソールの URL をクリックして、トレースの詳細ページに移動します。ceb9d5ba51bb6e89.jpeg

または、Firebase コンソールで、アプリを含むプロジェクトを選択します。左側のパネルで [リリースとモニタリング] セクションを見つけて、[パフォーマンス] をクリックします。

  • メインの [ダッシュボード] タブで、トレース テーブルまで下にスクロールし、[カスタム トレース] タブをクリックします。この表には、先ほど追加したカスタムコード トレースと、_app_start トレースなどのすぐに使用できるトレースが表示されます。
  • 2 つのカスタムコード トレース TestActivity-LoadTimeTestFragment-LoadTime を見つけます。いずれかの [期間] をクリックすると、収集されたデータの詳細が表示されます。

a0d8455c5269a590.png

  1. カスタムコード トレースの詳細ページには、トレースの期間(測定された読み込み時間)に関する情報が表示されます。

5e92a307b7410d8b.png

  1. カスタム画面トレースのパフォーマンス データも確認できます。
  • [ダッシュボード] のメインタブに戻り、トレース テーブルまで下にスクロールして、[画面レンダリング] タブをクリックします。この表には、先ほど追加したカスタム画面トレースと、MainActivity トレースなどの組み込みの画面トレースが表示されます。
  • カスタム画面トレース MainActivity-TestFragment を探します。トレース名をクリックすると、レンダリングが遅いフレームとフリーズしたフレームの集計データが表示されます。

ee7890c7e2c28740.png

10. 完了

これで完了です。Firebase Performance Monitoring を使用して、アクティビティとフラグメントの読み込み時間と画面レンダリングのパフォーマンスを測定できました。

達成したこと

次のステップ

Firebase Performance では、カスタム トレース以外にもアプリのパフォーマンスを測定する方法が用意されています。アプリ起動時間、フォアグラウンド アプリ、バックグラウンド アプリのパフォーマンス データを自動的に測定します。Firebase コンソールでこれらの指標を確認します。

また、Firebase Performance は自動 HTTP/S ネットワーク リクエスト モニタリングも提供します。これにより、コードを 1 行も記述することなく、ネットワーク リクエストを簡単に計測できます。アプリからネットワーク リクエストを送信して、Firebase コンソールで指標を確認していただけますか?

参考

カスタムコード トレースを使用して Activity/Fragment の読み込み時間と画面レンダリングのパフォーマンスを測定する方法を理解したところで、オープンソースのコードベースを調べて、アプリの一部である任意の Activity/Fragment の指標をすぐに取得できるかどうかを確認してみましょう。ご希望でしたら、お気軽に PR をお送りください :-)

11. ボーナス学習

アクティビティの読み込み中に何が起こっているかを把握すると、アプリのパフォーマンス特性をより深く理解できます。前のステップでは、アクティビティの読み込み中に何が起こるかを大まかに説明しましたが、次の図では各フェーズをより詳細に説明しています。

cd61c1495fad7961.png