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 に接続する
- [Android Studio] / [Help] >Android Studio と Firebase Assistant の最新バージョンを使用していることを確認するために、アップデートを確認してください。
- [ツール] >[Firebase] をクリックして [Assistant] ペインを開きます。
- [Performance Monitoring] を選択してアプリに追加し、[Performance Monitoring を使ってみる] をクリックします。
- [Firebase に接続] をクリックして、Android プロジェクトを Firebase に接続します(これにより、ブラウザに Firebase コンソールが開きます)。
- Firebase コンソールで [プロジェクトを追加] をクリックし、Firebase プロジェクト名を入力します(すでに Firebase プロジェクトがある場合は、代わりにその既存のプロジェクトを選択できます)。[続行] をクリックして利用規約に同意し、Firebase プロジェクトと新しい Firebase アプリを作成します。
- 次に、新しい Firebase アプリを Android Studio プロジェクトに接続するためのダイアログが表示されます。
- Android Studio に戻ると、[Assistant] ペインに、アプリが Firebase に接続したことの確認が表示されます。
Performance Monitoring をアプリに追加する
Android Studio の [Assistant] ペインで、[Add Performance Monitoring to your app] をクリックします。
[Accept Changes] ダイアログが表示されます。この後、必要な依存関係がすべて追加されていることを確認するために、Android Studio がアプリを同期します。
最後に、Android Studio の [Assistant] ペインに、すべての依存関係が正しく設定されていることを示す成功メッセージが表示されます。
追加の手順として、「(省略可)デバッグ ロギングを有効にする」の手順に沿ってデバッグ ロギングを有効にします。同じ手順が一般公開ドキュメントにも記載されています。
3. アプリを実行する
アプリと Performance Monitoring SDK が正常に統合されていれば、プロジェクトはコンパイルされます。Android Studio で [Run] >app を実行して、接続された Android デバイス/エミュレータでアプリをビルドして実行します。
このアプリには、対応する Activity と Fragment に移動できる下記の 2 つのボタンがあります。
この Codelab の以降のステップでは、アクティビティまたはフラグメントの読み込み時間と画面レンダリングのパフォーマンスを測定する方法を学びます。
4. アクティビティまたはフラグメントの読み込みについて
このステップでは、アクティビティまたはフラグメントの読み込み中にシステムが何を行っているかを確認します。
アクティビティの読み込みについて
Activity の場合、読み込み時間は、Activity オブジェクトが作成されてから、最初のフレームが画面に完全に描画されるまでの時間として定義されます(このタイミングがユーザーにアクティビティの完全な UI が初めて表示されるとき)。アプリが完全に描画されたかどうかを測定するには、reportFullyDrawn()
メソッドを使用して、アプリを起動してからすべてのリソースとビュー階層が完全に表示されるまでの経過時間を測定します。
大まかに言うと、アプリが startActivity(Intent)
を呼び出すと、システムにより以下のプロセスが自動的に実行されます。各プロセスの完了には時間がかかります。そのため、アクティビティが作成されてから、ユーザーの画面にアクティビティの UI が表示されるまでの時間が長くなります。
Fragment の読み込みについて
Activity と同様に、Fragment の読み込み時間は、Fragment がホスト Activity にアタッチされてから、Fragment View の First Frame が完全に画面に描画されるまでの時間として定義されます。
5. アクティビティの読み込み時間を測定する
最初のフレームで遅延が発生すると、ユーザー エクスペリエンスが低下する可能性があります。そのため、初期読み込みの遅延がユーザーにどの程度発生しているかを把握することが重要です。この読み込み時間を測定するには、カスタム コード トレースを使用します。
- 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);
// ...
}
onDrawingStart()
とonDrawingFinish()
の 2 つのコールバックを持つFistDrawListener
の実装が含まれています(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();
}
});
- アプリを再実行します。次に、[Logging トレース指標] で logcat をフィルタします。
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 の測定方法と似ていますが、若干の違いがあります。ここでも、カスタム コード トレースをインストルメント化します。
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
を登録します。次に、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();
}
});
- アプリを再実行します。次に、[Logging トレース指標] で logcat をフィルタします。
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. Fragment の遅いフレーム/フリーズしたフレームを測定する
Firebase Performance Monitoring は、アクティビティの遅いフレームやフリーズしたフレームを自動的にキャプチャします(ただし、Hardware Accelerated の場合に限ります。ただし、この機能は現在、Fragment では使用できません。Fragment の遅いフレーム/フリーズしたフレームは、Fragment のライフサイクルの onFragmentAttached()
コールバックと onFragmentDetached()
コールバックの間のアクティビティ全体の遅いフレーム/フリーズしたフレームとして定義されます。
AppStateMonitor
クラス(Activity の画面トレースの記録を担当する Performance Monitoring SDK の一部)を参考に、ScreenTrace
クラス(この Codelab ソースコード リポジトリの一部)を実装しました。ScreenTrace
クラスをアクティビティの FragmentManager
のライフサイクル コールバックに接続して、遅いフレームやフリーズしたフレームをキャプチャできます。このクラスには、次の 2 つの公開 API が用意されています。
recordScreenTrace()
: 画面トレースの記録を開始します。sendScreenTrace()
: 画面トレースの記録を停止し、カスタム指標を接続して合計フレーム数、低速フレーム数、フリーズしたフレーム数をログに記録します。
これらのカスタム指標を追加すると、Fragment の画面トレースをアクティビティの画面トレースと同じように処理し、Firebase コンソールの [Performance] ダッシュボードに他の画面レンダリング トレースとともに表示できます。
Fragment の画面トレースをログに記録する方法は次のとおりです。
- 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);
// ...
}
- 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);
}
};
- アプリを再実行します。次に、[
LOAD FRAGMENT
] ボタンをタップします。数秒待ってから、下部のナビゲーション バーにある [back button
] をクリックします。
logcat を [Logging trace metric] でフィルタし、次のようなログを探します。
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
🎉? おめでとうございます!Fragment で遅いフレーム/フリーズしたフレームを測定し、そのデータを Firebase Performance Monitoring に報告することができました。記録された指標は、この Codelab の後半で Firebase コンソールで確認できます。
9. Firebase コンソールで指標を確認する
- logcat で、Firebase コンソールの URL をクリックして、トレースの詳細ページに移動します。
または、Firebase コンソールで、アプリが含まれているプロジェクトを選択します。左側のパネルで [リリースと[Monitor] セクションに移動し、[Performance] をクリックします。
- メインの [ダッシュボード] タブでトレース テーブルまで下にスクロールし、[カスタム トレース] タブをクリックします。この表には、先ほど追加したカスタム コード トレースと、
_app_start
トレースなどのすぐに使えるトレースが表示されます。 - 2 つのカスタム コード トレース
TestActivity-LoadTime
とTestFragment-LoadTime
を見つけます。いずれかの [Duration] をクリックすると、収集されたデータの詳細が表示されます。
- カスタムコード トレースの詳細ページには、トレースの継続時間に関する情報(測定された読み込み時間)が表示されます。
- カスタム画面トレースのパフォーマンス データを表示することもできます。
- メインの [ダッシュボード] タブに戻り、トレース テーブルまで下にスクロールして、[画面のレンダリング] タブをクリックします。この表には、前に追加したカスタム画面トレースと、
MainActivity
トレースなどのすぐに使用できる画面トレースが表示されます。 - カスタム画面トレース
MainActivity-TestFragment
を見つけます。トレース名をクリックすると、レンダリングが遅いフレームやフリーズしたフレームの集計データが表示されます。
10. 完了
これで完了です。Firebase Performance Monitoring を使用して、アクティビティとフラグメントの読み込み時間と画面レンダリングのパフォーマンスを測定することができました。
これまでに達成したこと
- Firebase Performance Monitoring をサンプルアプリに統合しました
- ビューの読み込みのライフサイクルを理解しました
- カスタムコード トレースを追加して、アクティビティとフラグメントの両方の読み込み時間を測定しました。
- カスタム指標を使用したカスタム スクリーン トレースを追加して、遅いフレームやフリーズしたフレームを記録した
次のステップ
Firebase Performance では、カスタム トレース以外にも、さまざまな方法でアプリのパフォーマンスを測定できます。アプリの起動時間、フォアグラウンドのアプリ、バックグラウンドのアプリのパフォーマンス データを自動的に測定します。これらの指標を Firebase コンソールで確認してみましょう。
また、Firebase Performance は、HTTP/S ネットワーク リクエストの自動モニタリングも提供しています。これにより、コードを 1 行も記述せずにネットワーク リクエストを簡単に計測できます。アプリからネットワーク リクエストを送信して、Firebase コンソールで指標をご確認いただけますか?
参考
カスタムコード トレースを使用してアクティビティやフラグメントの読み込み時間と画面レンダリングのパフォーマンスを測定する方法がわかったところで、Google のオープンソース コードベースを参照して、アプリの一部であるアクティビティ / フラグメントについて、すぐに指標を取得できるかどうか確認してみましょう。ご希望の場合は PR までお願いします。
11. ボーナス学習
アクティビティの読み込み中に何が起こっているかを理解すると、アプリのパフォーマンス特性をより深く理解できます。前のステップでは、アクティビティの読み込み中に行われる処理の概要について説明しましたが、次の図では、各フェーズをより詳細に説明しています。