CameraX のアーキテクチャ

Jetpack に追加された CameraX を使用すると、Camera2 API の機能を簡単に利用することができます。このトピックでは、CameraX の構造、API との連携方法、ライフサイクルとの連携方法、ユースケースを組み合わせる方法を含め、CameraX のアーキテクチャについて説明します。

CameraX の構造

デベロッパーは CameraX を使用することで、ユースケースと呼ばれる抽象化を通じてデバイスのカメラとインターフェースすることができます。現在、以下のユースケースを利用できます。

  • プレビュー: プレビュー用の SurfaceTexture を準備します。
  • 画像解析: CPU からアクセス可能な解析用(機械学習用など)のバッファを提供します。
  • 画像キャプチャ: 写真をキャプチャして保存します。

ユースケースは、組み合わせて同時にアクティブにすることができます。たとえばアプリにおいて、プレビュー ユースケースを使用してカメラから見える画像をユーザーが確認できるようにし、画像解析ユースケースで被写体の人が笑顔かどうかを判断して、画像キャプチャ ユースケースで被写体の人が笑顔になったときに写真を撮る、といったことが可能です。

API モデル

ライブラリを使用する場合は以下を指定します。

  • 使用するユースケースと設定オプション
  • 出力データの処理方法(リスナーをアタッチする)
  • カメラを有効にするタイミング、データを生成するタイミングなどの対象とするフロー(ユースケースを Android アーキテクチャのライフサイクルにバインドする)

ユースケースの設定オブジェクトは、set() メソッドを使用して設定され、build() メソッドで確定されます。各ユースケース オブジェクトは、ユースケースに固有の一連の API を提供します。たとえば画像キャプチャ ユースケースは、takePicture() メソッドの呼び出しを提供します。

アプリでは、onResume()onPause() で明示的に開始および停止メソッドを呼び出すのではなく、CameraX.bindToLifecycle() を使用して、カメラを関連付けるライフサイクルを指定します。起動、シャットダウン、データの生成は、指定された Android アーキテクチャのライフサイクルによって管理されます。このライフサイクルからカメラのキャプチャ セッションを設定するタイミングが CameraX に通知され、ライフサイクルの遷移に合わせてカメラの状態が適切に変更されます。

各ユースケースの実装手順については、プレビューを実装する画像を解析する写真を撮るをご覧ください。

API モデルの例

プレビュー ユースケースは、ディスプレイ用の SurfaceTexture を提供します。アプリは次のコードを使用して、設定オプションが指定されたユースケースを作成します。

Kotlin

    val previewConfig = PreviewConfig.Builder().build()
    val preview = Preview(previewConfig)

    val textureView: TextureView = findViewById(R.id.textureView)

    // The output data-handling is configured in a listener.
    preview.setOnPreviewOutputUpdateListener { previewOutput ->
        textureView.surfaceTexture = previewOutput.surfaceTexture
    }

    // The use case is bound to an Android Lifecycle with the following code.
    CameraX.bindToLifecycle(this as LifecycleOwner, preview)
    

Java

    PreviewConfig config = new PreviewConfig.Builder().build();
    Preview preview = new Preview(config);

    TextureView textureView = findViewById(R.id.textureView);

    preview.setOnPreviewOutputUpdateListener(
        new Preview.OnPreviewOutputUpdateListener() {
            @Override
            public void onUpdated(Preview.PreviewOutput previewOutput) {
                // The output data-handling is configured in a listener.
                textureView.setSurfaceTexture(previewOutput.getSurfaceTexture());
                // Your custom code here.
            });
    });

    // The use case is bound to an Android Lifecycle with the following code.
    CameraX.bindToLifecycle((LifecycleOwner) this, preview);
    

その他のサンプルコードについては、CameraX の公式サンプルアプリをご覧ください。

CameraX のライフサイクル

CameraX はライフサイクルを監視して、カメラを起動するタイミング、キャプチャ セッションを作成するタイミング、停止およびシャットダウンするタイミングを決定します。ユースケースの API は、進行状況を監視するためのメソッド呼び出しとコールバックを提供します。

ユースケースを組み合わせるで説明するように、ユースケースを組み合わせて 1 つのライフサイクルにまとめることができます。組み合わせることができないユースケースをアプリでサポートする必要がある場合は、以下のいずれかを行うことができます。

  • 互換性のあるユースケースを複数のフラグメントにグループ化して、フラグメントを切り替える
  • カスタムのライフサイクル コンポーネントを作成し、それを使ってカメラのライフサイクルを手動で管理する

ビューとカメラの各ユースケースのライフサイクル オーナーを分ける場合(カスタム ライフサイクルを使用する場合やフラグメントを保持する場合)は、すべてのユースケースを CameraX からバインド解除する必要があります。そのためには、CameraX.unbindAll() を使用するか、または各ユースケースを個別にバインド解除します。また、標準のライフサイクル用の onCreate メソッドでユースケースをバインドしている場合は、キャプチャ セッションの開始と終了、ユースケースのバインド解除を CameraX に管理させることができます。

すべてのカメラ機能が 1 つのライフサイクル対応コンポーネント(AppCompatActivityAppCompat フラグメントなど)のライフサイクルに対応している場合、使用するすべてのユースケースをバインドするときにそのコンポーネントのライフサイクルを利用することで、ライフサイクル対応コンポーネントがアクティブなときにカメラ機能をすぐに使用できます。アクティブでないときは、安全にカメラ機能を破棄してリソースの消費を避けられます。

カスタムの LifecycleOwner

高度な手法として、標準の Android LifecycleOwner を使用する代わりに、カスタムの LifecycleOwner を作成することで、CameraX のセッション ライフサイクルをアプリで明示的に制御できます。

次のコードサンプルは、シンプルなカスタムの LifecycleOwner を作成する方法を示しています。

Kotlin

    class CustomLifecycle : LifecycleOwner {
        private val lifecycleRegistry: LifecycleRegistry

        init {
            lifecycleRegistry = LifecycleRegistry(this);
            lifecycleRegistry.markState(Lifecycle.State.CREATED)
        }
        ...
        fun doOnResume() {
            lifecycleRegistry.markState(State.RESUMED)
        }
        ...
        override fun getLifecycle(): Lifecycle {
            return lifecycleRegistry
        }
    }
    

Java

    public class CustomLifecycle implements LifecycleOwner {
        private LifecycleRegistry mLifecycleRegistry;
        public CustomLifecycle() {
            mLifecycleRegistry = new LifecycleRegistry(this);
            mLifecycleRegistry.markState(Lifecycle.State.CREATED);
        }
       ...
       public void doOnResume() {
            mLifecycleRegistry.markState(State.RESUMED);
        }
       ...
        public Lifecycle getLifecycle() {
            return mLifecycleRegistry;
        }
    }
    

この LifecycleOwner を使用すると、アプリのコード内の任意の位置で状態遷移を行うことができます。この機能をアプリに実装する方法について詳しくは、カスタムの LifecycleOwner の実装をご覧ください。

ユースケースを組み合わせる

複数のユースケースを同時に実行することができます。ユースケースはライフサイクルに連続的にバインドできますが、CameraX.bindToLifecycle() の 1 回の呼び出しですべてのユースケースをバインドするほうが効率的です。設定の変更に関するおすすめの方法について詳しくは、設定の変更への対処をご覧ください。

次のコードサンプルでは、アプリで 2 つのユースケースを作成して同時に実行するように指定しています。また、両方のユースケースに使用するライフサイクルを指定して、どちらもそのライフサイクルに従って開始および停止するようにしています。

Kotlin

    val imageCapture: ImageCapture

    override fun onCreate() {
        val previewConfig = PreviewConfig.Builder().build()
        val imageCaptureConfig = ImageCaptureConfiguration.Builder().build()

        val imageCapture = ImageCapture(imageCaptureConfig)
        val preview = Preview(previewConfig)

        val textureView = findViewById(R.id.textureView)

        preview.setOnPreviewOutputUpdateListener { previewOutput ->
            textureView.surfaceTexture = previewOutput.surfaceTexture
        }

        CameraX.bindToLifecycle(this as LifecycleOwner, preview, imageCapture)
    }
    

Java

    private ImageCapture imageCapture;

    void onCreate() {
        PreviewConfig previewConfig = new PreviewConfig.Builder().build();
        imageCaptureConfig imageCaptureConfig =
            new ImageCaptureConfig.Builder().build();

        imageCapture = new ImageCapture(imageCaptureConfig);
        preview = new Preview(previewConfig);

        TextureView textureView = findViewById(R.id.textureView);

        preview.setOnPreviewOutputUpdateListener(
            previewOutput -> {
                textureView.setSurfaceTexture(previewOutput.getSurfaceTexture());
        });

        CameraX.bindToLifecycle((LifecycleOwner) this, preview, imageCapture);
    }
    

サポートされている設定の組み合わせを以下に示します。

プレビュー 画像解析 画像キャプチャ ユースケースの組み合わせ
プレビューの表示、写真の撮影、画像ストリームの解析を行います。
写真の撮影、画像ストリームの解析を行います。
ストリーミングされている画像の解析結果に基づいて視覚効果を適用してプレビューを表示します。
カメラから見える画像を表示し、ユーザーの操作に基づいて写真を撮影します。

権限

アプリには CAMERA 権限が必要です。画像をファイルに保存するには、Android Q 以降を実行しているデバイスを除き、WRITE_EXTERNAL_STORAGE 権限も必要になります。

アプリの権限の設定方法について詳しくは、アプリの権限をリクエストするをご覧ください。

要件

CameraX には以下の最小バージョン要件があります。

  • Android API レベル 21
  • Android アーキテクチャ コンポーネント 1.1.1

ライフサイクル対応のアクティビティには、FragmentActivity または AppCompatActivity を使用します。

依存関係の宣言

CameraX への依存関係を追加するには、Google の Maven リポジトリをプロジェクトに追加する必要があります。

以下に示すように、プロジェクトの build.gradle ファイルを開いて google() リポジトリを追加します。

    allprojects {
      repositories {
        google()
        jcenter()
      }
    }
    

各モジュールの build.gradle ファイルに以下を追加します。

    dependencies {
        // CameraX core library.
        def camerax_version = "1.0.0-alpha05"
        implementation "androidx.camera:camera-core:${camerax_version}"
        // If you want to use Camera2 extensions.
        implementation "androidx.camera:camera-camera2:${camerax_version}"

    }
    

これらの要件に準拠するようにアプリを設定する方法について詳しくは、依存関係の宣言をご覧ください。

参考情報

CameraX について詳しくは、以下の参考情報をご確認ください。

コードラボ

  • CameraX のスタートガイド
  • コードサンプル

  • CameraX の公式サンプルアプリ