Firebase Performance Monitoring で読み込み時間と画面のレンダリングを測定する

1. はじめに

最終更新日: 2021 年 3 月 11 日

ビューのパフォーマンスを測定する必要があるのはなぜですか?

ビューは Android アプリの重要な要素であり、ユーザー エクスペリエンスに直接影響します。たとえば、アクティビティまたはフラグメントには、ユーザーが操作するビュー コンポーネントを保持する UI が含まれています。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 プラグインを使用して、Firebase プロジェクトに Android アプリを登録し、Android プロジェクトに必要な Firebase 構成ファイル、プラグイン、依存関係を追加します。これらはすべて Android Studio から行います。

アプリを Firebase に接続する

  1. [Android Studio] / [Help] >Android Studio と Firebase Assistant の最新バージョンを使用していることを確認するために、アップデートを確認してください。
  2. [ツール] >[Firebase] をクリックして [Assistant] ペインを開きます。

e791bed0999db1e0.png

  1. [Performance Monitoring] を選択してアプリに追加し、[Performance Monitoring を使ってみる] をクリックします。
  2. [Firebase に接続] をクリックして、Android プロジェクトを Firebase に接続します(これにより、ブラウザに Firebase コンソールが開きます)
  3. Firebase コンソールで [プロジェクトを追加] をクリックし、Firebase プロジェクト名を入力します。(Firebase プロジェクトがすでにある場合は、その既存のプロジェクトを選択することもできます)。[続行] をクリックして利用規約に同意し、Firebase プロジェクトと新しい Firebase アプリを作成します。
  4. 次に、新しい Firebase アプリを Android Studio プロジェクトに接続するためのダイアログが表示されます。

42c498d28ead2b77.png

  1. Android Studio に戻り、[Assistant] ペインに、アプリが 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] >app を実行して、接続された Android デバイス/エミュレータでアプリをビルドして実行します。

このアプリには、対応する Activity と Fragment に移動できる下記の 2 つのボタンがあります。

410d8686b4f45c33.png

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

4. アクティビティまたはフラグメントの読み込みについて

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

アクティビティの読み込みについて

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

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

c20d14b151549937.png

Fragment の読み込みについて

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

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. Fragment の読み込み時間を測定する

Fragment の読み込み時間の測定は、Activity の測定方法と似ていますが、若干の違いがあります。ここでも、カスタム コード トレースを計測します。

  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 を登録します。次に、Activity の例と同様に、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)

🎉 おめでとうございます。Fragment の読み込み時間を測定し、そのデータを Firebase Performance Monitoring に報告することができました。記録された指標は、この Codelab の後半で Firebase コンソールで確認します。

7. 画面レンダリングと遅い / フリーズしたフレームの理解

UI レンダリングとは、アプリからフレームを生成して画面に表示することです。ユーザーのアプリ操作をスムーズにするためには、16 ミリ秒未満でフレームをレンダリングし、1 秒あたり 60 フレームを達成する必要があります(なぜ 60 fps が必要なのか)。アプリの UI レンダリングが遅い場合、システムはフレームをスキップせざるを得ず、ユーザーはアプリの動作がスムーズでないと感じます。これをジャンクと呼びます。

同様に、フリーズしたフレームは、レンダリングに 700 ミリ秒より長くかかる UI フレームです。フレームがレンダリングされている間、アプリが停止しているように見え、ユーザー入力にほぼ 1 秒間反応しないため、この遅延は問題です。

8. フラグメントの遅いフレーム / フリーズしたフレームを測定する

Firebase Performance Monitoring は、アクティビティの遅いフレームやフリーズしたフレームを自動的にキャプチャします(ただし、ハードウェア アクセラレーションが有効な場合のみ)。ただし、この機能は現在、フラグメントではご利用いただけません。Fragment の遅いフレーム/フリーズしたフレームは、Fragment のライフサイクルの onFragmentAttached() コールバックと onFragmentDetached() コールバックの間のアクティビティ全体の遅いフレーム/フリーズしたフレームとして定義されます。

AppStateMonitor クラス(Activity の画面トレースの記録を担当する Performance Monitoring SDK の一部)を参考に、ScreenTrace クラス(この Codelab のソースコード リポジトリの一部)を実装しました。ScreenTrace クラスをアクティビティの FragmentManager のライフサイクル コールバックに接続して、遅いフレームやフリーズしたフレームをキャプチャできます。このクラスには、次の 2 つの公開 API が用意されています。

  • recordScreenTrace(): 画面トレースの記録を開始します。
  • sendScreenTrace(): 画面トレースの記録を停止し、カスタム指標を添付して、合計フレーム数、遅いフレーム数、フリーズしたフレーム数をログに記録します。

これらのカスタム指標をアタッチすると、Fragment の画面トレースをアクティビティの画面トレースと同様に処理し、Firebase コンソールのパフォーマンス ダッシュボードで他の画面レンダリング トレースとともに表示できます。

フラグメントの画面トレースをロギングする方法は次のとおりです。

  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() コールバックをオーバーライドします。すでにこの処理を完了しております。画面トレースの記録は 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 を [Logging trace metric] でフィルタし、次のようなログを探します。

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

🎉? おめでとうございます!Fragment で遅いフレーム/フリーズしたフレームを測定し、そのデータを Firebase Performance Monitoring に報告することができました。記録された指標は、この Codelab の後半で Firebase コンソールで確認できます。

9. Firebase コンソールで指標を確認する

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

または、Firebase コンソールで、アプリが含まれているプロジェクトを選択します。左側のパネルで [リリースと[Monitor] セクションに移動し、[Performance] をクリックします。

  • メインの [ダッシュボード] タブで、トレース テーブルまで下にスクロールし、[カスタム トレース] タブをクリックします。この表には、先ほど追加したカスタムコード トレースに加えて、_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