システムアプリでの ViewCapture

ViewCapture はフックされたウィンドウに関連付けられたビューのプロパティ(位置、サイズ、スケール、表示状態など)をキャプチャするソフトウェア ツールです。ViewCapture ではウィンドウ内のさまざまなビューに関する情報とビューのプロパティをキャプチャできるため、特定の瞬間のユーザー エクスペリエンスの状態を把握し、変更の履歴をトラッキングできます。

画面の録画でも特定の瞬間のビューの状態を可視化して、ビューがどのように変化しているかを確認できますが、膨大な量の CPU リソースが必要となるため、パフォーマンスに影響を及ぼします。ViewCapture ツールの場合、リソースへの影響が少ないため、より頻繁に使用できます。さらに、ViewCapture ではビューレベルでフレームごとに表示できるため、画面の録画に比べて、特定の瞬間のビューの状態をより簡単に確認できます。

このページでは、システムアプリでの ViewCapture のオンボーディングの方法について説明します。

使用

ViewCapture.javaonDrawListener のインスタンスを実装し、描画プロセス時の ViewCapture トレースを収集します。フレームの再描写のたびに、ウィンドウのルートビューから始まる、ビューのツリー階層のトラバーサルがトリガーされます。ViewCapture は View.java 公開ゲッター メソッドを使用して値を取得し、バックグラウンド スレッドにコピーして、パフォーマンスを高めます。ViewCapture 実装はこのプロセスを最適化するために、captureViewTree を使用して、ビューがダーティか無効かどうかを確認し、ビュー階層全体のトラバーサルを回避します。captureViewTree はシステムアプリでのみ利用できる、UnsupportedAppUsage API の一つです。この API の使用はターゲット SDK のバージョンのアプリに制限されます。

制限事項

次のセクションでは、ViewCapture の実行時のパフォーマンスとメモリの制限について説明します。

パフォーマンス

ViewCapture パフォーマンスのメインスレッドの平均オーバーヘッドは 195 μs です。ただし、最悪のケースでは約 5 ms となる場合があります。Perfetto トレースの vc#onDraw スライスをご参照ください。

オーバーヘッド コストは主に次のアクションにより発生します。

  1. 階層のトラバーサルはプルーニングされているときでも 50 μs かかります。
  2. フリーリスト アロケータからオブジェクトを取得して、ビュー プロパティのコピーを格納すると 20 μs かかります。
  3. ゲッター関数を使用して各プロパティの値を取得すると、ビューごとに追加で多くの関数呼び出しが必要となり、110 μs かかります。

このため、ViewCapture の常時トレース(AOT)を有効にすると、システム パフォーマンスに悪影響を及ぼし、ジャンクにつながります。このようなパフォーマンスとメモリの制限があるため、このアプローチに AOT は適していません。ViewCapture はラボとローカルでのデバッグにのみ使用することをおすすめします。

メモリ

Perfetto での ViewCapture トレース メソッドは、単一のリングバッファを使用します。これはメモリ使用量が事前に決まっているため、過剰なメモリの使用を抑制できます。このアプローチでは、各ウィンドウに個別のリングバッファを使用しないようにして、過剰なメモリの消費を防ぎます。ただし、各フレームの Perfetto のそれぞれの状態に関するビュー階層全体を保存する問題は解消されません。NexusLauncher などの単一ウィンドウを記録すると、10 MB バッファで 30 秒以上の ViewCapture データが生成されます。ただし、システム UI から 30 以上のウィンドウをキャプチャする場合、より大きなバッファが必要になるか、大幅に録画時間を短縮する必要があります。

手順

次の手順に沿って、システムアプリでの ViewCapture のオンボーディングができます。

  1. ランチャー コードのとおり、依存関係を Android.bp ファイルに追加します。

    android_library {
        name: "YourLib",
        static_libs: [
              ...
            "//frameworks/libs/systemui:view_capture",
              ...
        ],
        platform_apis: true,
        privileged: true,
    }
    
  2. ウィンドウを作成するときに、次のように ViewCapture インスタンスを作成します。

    • 例 1:

      private SafeCloseable mViewCapture;
      
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        ...
        mViewCapture = ViewCaptureFactory.getInstance(this).startCapture(getWindow());
      }
      
    • 例 2:

      private SafeCloseable mViewCapture;
      
      @Override
      protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (enableViewCaptureTracing()) {
            mViewCaptureCloseable = ViewCaptureFactory.getInstance(getContext())
              .startCapture(getRootView(), ".NotificationShadeWindowView");
        }
        ...
      }
      
  3. 次の例のように、ウィンドウを破棄するときに ViewCapture インスタンスを閉じます。

    • 例 1:

      @Override
      public void onDestroy() {
        ...
        if (mViewCapture != null) mViewCapture.close();
      }
      
    • 例 2:

      @Override
      protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mViewCaptureCloseable != null) {
            mViewCaptureCloseable.close();
       }
        ...
      }