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

1. はじめに

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

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

ビューは、ユーザー エクスペリエンスに直接影響する Android アプリの重要な要素です。たとえば、アクティビティやフラグメントには、ユーザーが操作する View コンポーネントを保持する UI が含まれています。画面上に UI が完全に描画されるまで、ユーザーには UI のコンテンツ全体が表示されません。画面が遅い、フリーズしていると、ユーザーによるアプリの操作が直接妨げられ、ユーザー エクスペリエンスが低下します。

Firebase Performance Monitoring では、これらのパフォーマンス指標がすぐに提供されますか?

Firebase Performance Monitoring は、アプリの開始時間(最初のアクティビティのみの読み込み時間)や画面レンダリングのパフォーマンス(アクティビティでは遅いフレームやフリーズしたフレームがフラグメントではない)など、一部のパフォーマンス データをすぐに自動的にキャプチャします。しかし、業界アプリは通常、多くのアクティビティではなく、1 つの Activity と複数の Fragment を持ちます。また、多くのアプリは通常、より複雑なユースケース向けに独自のカスタムビューを実装しています。そのため、アプリにカスタムコード トレースをインストルメント化して、アクティビティとフラグメントの両方の読み込み時間と画面レンダリングのパフォーマンスを測定する方法を理解しておくと役に立ちます。この 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 プロジェクトに登録し、必要な Firebase 構成ファイル、プラグイン、依存関係を Android プロジェクトに追加します。これらはすべて、すべて Android Studio 内から行います。

アプリを Firebase に接続する

  1. [Android Studio] > [Help] > [Check for updates] に移動し、Android Studio と Firebase Assistant の最新バージョンを使用していることを確認します。
  2. [Tools] > [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] をクリックします。

[Accept Changes] ダイアログが表示されます。その後、Android Studio はアプリを同期して、必要な依存関係がすべて追加されたことを確認します。

9b58145acc4be030.png

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

aa0d46fc944e0c0b.png

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

3. アプリを実行する

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

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

410d8686b4f45c33.png

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

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

このステップでは、アクティビティやフラグメントの読み込み中のシステムの動作を学習します。

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

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

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

c20d14b151549937.png

Fragment の読み込みについて

アクティビティと同様に、Fragment の読み込み時間は、Fragment がホスト アクティビティに接続されてから、Fragment View の First Frame が画面に完全に描画されるまでの時間として定義されます。

5. アクティビティの読み込み時間を測定する

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

  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() メソッドで追加されたビューを取得します。
@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. onDrawingStart()onDrawingFinish() の 2 つのコールバックを持つ FistDrawListener の実装を組み込んでいます(onDrawingStart()onDrawingFinish() 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 トレース指標」でフィルタします。[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 という名前を付けます。

前のステップで説明したように、フラグメント オブジェクトはいつでも作成できますが、このオブジェクトがアクティブになるのは、そのホスト アクティビティに接続されたときのみです。

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 トレース指標」でフィルタします。[LOAD FRAGMENT] ボタンをタップして、次のようなログを探します。
I/FirebasePerformance: Logging trace metric: TestFragment-LoadTime (duration: XXXms)

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

7. 画面レンダリングと、遅いフレーム/フリーズしたフレームの概要を理解する

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

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

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

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

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

  • recordScreenTrace(): 画面トレースの記録を開始します
  • sendScreenTrace(): 画面トレースの記録を停止し、合計フレーム数、遅いフレーム数、フリーズしたフレーム数を記録するカスタム指標をアタッチします。

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

Fragment の画面トレースを記録する方法は次のとおりです。

  1. Fragment をホストする Activity で 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. Fragment を読み込むときに、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 を [Logging トレース指標] でフィルタし、次のようなログを探します。

I/FirebasePerformance: Logging trace metric: _st_MainActivity-TestFragment (duration: XXXms)

logcat を「FireperfViews」でフィルタし、次のようなログを探します。

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)を見つけます。どちらかの [Duration] をクリックすると、収集されたデータの詳細が表示されます。

a0d8455c5269a590.png

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

5e92a307b7410d8b.png

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

ee7890c7e2c28740.png

10. 完了

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

これまでの成果

次のステップ

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

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

参考

カスタムコード トレースを使用して、アクティビティ/フラグメントの読み込み時間と画面レンダリングのパフォーマンスを測定する方法がわかったので、オープンソースのコードベースを調べて、アプリの一部であるアクティビティ/フラグメントについて、これらの指標をすぐにキャプチャできるかどうかを確認してみましょう。ご希望であれば、お気軽に PR を送信してください。

11. ボーナス学習

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

cd61c1495fad7961.png