Google 致力于为黑人社区推动种族平等。查看具体举措

CameraX 架构

CameraX 是对 Jetpack 的补充,可让您更轻松地利用 Camera2 API 的功能。本主题介绍了 CameraX 的架构,包括其结构、如何与 API 搭配使用、如何与生命周期配合以及如何组合各种用例。

CameraX 结构

开发者使用 CameraX,借助名为“用例”的抽象概念与设备的相机进行交互。目前提供的用例如下:

  • 预览:接受用于显示预览的 Surface,例如 PreviewView
  • 图片分析:提供 CPU 可访问的缓冲区以进行分析(例如进行机器学习)。
  • 图片拍摄:拍摄并保存照片。

不同用例可以相互组合使用,也可以同时处于活动状态。例如,应用中可以加入预览用例供用户查看进入相机视野的画面,加入图片分析用例来确定照片里的人物是否在微笑,以及包含一个图片拍摄用例以便在人物微笑时拍摄照片。

API 模型

如需使用该库,请指定以下内容:

  • 具有配置选项的所需用例。
  • 通过附加监听器来指定如何处理输出数据。
  • 通过将用例绑定到 Android 架构生命周期来指定目标流程,例如何时启用相机及何时生成数据。

您可以使用 set() 方法配置用例,并使用 build() 方法完成这些用例。每个用例对象都提供一组特定于该用例的 API。例如,图片拍摄用例会提供 takePicture() 方法调用。

应用使用 cameraProvider.bindToLifecycle() 指定要与相机关联的生命周期,而不是将特定的开始和结束方法调用放入 onResume()onPause()。之后,该生命周期会告知 CameraX 何时配置相机拍摄会话并确保相机状态随生命周期的转换相应地更改。

如需了解每个用例的实现步骤,请参阅实现预览分析图片图片捕获

API 模型示例

预览用例会与 Surface 互动以进行展示。应用使用以下代码创建具有配置选项的用例:

Kotlin

    val preview = Preview.Builder().build()
    val viewFinder: PreviewView = findViewById(R.id.previewView)

    // The use case is bound to an Android Lifecycle with the following code
    val camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)

    // PreviewView creates a surface provider and is the recommended provider
    preview.setSurfaceProvider(viewFinder.createSurfaceProvider(camera.cameraInfo))
    

Java

    Preview preview = new Preview.Builder().build();
    PreviewView viewFinder = findViewById(R.id.view_finder);

    // The use case is bound to an Android Lifecycle with the following code
    Camera camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview);

    // PreviewView creates a surface provider, using a Surface from a different
    // kind of view will require you to implement your own surface provider.
    preview.previewSurfaceProvider = viewFinder.createSurfaceProvider(camera.getCameraInfo());
    

如需查看更多示例代码,请参阅官方 CameraX 示例应用

CameraX 生命周期

CameraX 会观察生命周期以确定何时打开相机、何时创建拍摄会话以及何时停止和关闭。用例 API 提供方法调用和回调来监控进度情况。

组合用例中所述,您可以将多个用例一起绑定到单个生命周期。当您的应用需要支持无法组合的用例时,您可以执行以下某项操作:

  • 将兼容的用例划分到多个 Fragment 中,然后在 Fragment 之间进行切换
  • 创建自定义生命周期组件并用其手动控制相机生命周期

如果您将视图和相机用例的生命周期所有者分离(例如,如果您使用自定义生命周期或保留片段 ),那么您必须确保通过使用CameraX.unbindAll()或逐个取消绑定 或者,当您将用例绑定到生命周期时,可以让 CameraX 管理拍摄会话的开启和关闭操作以及用例的取消绑定操作。

如果所有相机功能都对应于单个具有生命感知能力的组件(例如 AppCompatActivityAppCompat Fragment)的生命周期,那么在绑定所有所需用例时,使用相应组件的生命周期可确保相机功能在具有感知生命周期能力的组件处于活动状态时准备就绪且可安全处置,否则不消耗任何资源。

自定义 LifecycleOwner

对于高级用例,您可以创建自定义 LifecycleOwner,以使您的应用显式控制 CameraX 会话生命周期,而不是将其绑定到标准 Android LifecycleOwner

以下代码示例展示了如何创建简单的自定义 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 lifecycleRegistry;
        public CustomLifecycle() {
            lifecycleRegistry = new LifecycleRegistry(this);
            lifecycleRegistry.markState(Lifecycle.State.CREATED);
        }
       ...
       public void doOnResume() {
            lifecycleRegistry.markState(State.RESUMED);
        }
       ...
        public Lifecycle getLifecycle() {
            return lifecycleRegistry;
        }
    }
    

借助此 LifecycleOwner,您的应用可以将状态转换放在代码中的所需位置。如需详细了解如何在应用中实现此功能,请参阅实现自定义 LifecycleOwner

并发用例

多个用例可以同时运行。虽然可以将用例依序绑定到生命周期,但最好通过一次调用 CameraProcessProvider.bindToLifecycle() 来绑定所有用例。如需详细了解配置更改的最佳做法,请参阅处理配置更改

在以下代码示例中,应用指定了要创建并同时运行的两个用例,还指定了同时用于这两个用例的生命周期,以便它们根据生命周期启动和停止。

Kotlin

    private lateinit var imageCapture: ImageCapture

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

        cameraProviderFuture.addListener(Runnable {
            // Camera provider is now guaranteed to be available
            val cameraProvider = cameraProviderFuture.get()

            // Set up the preview use case to display camera preview.
            val preview = Preview.Builder().build()

            // Set up the capture use case to allow users to take photos.
            imageCapture = ImageCapture.Builder()
                    .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                    .build()

            // Choose the camera by requiring a lens facing
            val cameraSelector = CameraSelector.Builder()
                    .requireLensFacing(CameraSelector.LENS_FACING_FRONT)
                    .build()

            // Attach use cases to the camera with the same lifecycle owner
            val camera = cameraProvider.bindToLifecycle(
                    this as LifecycleOwner, cameraSelector, preview, imageCapture)

            // Connect the preview use case to the previewView
            preview.setSurfaceProvider(
                    previewView.createSurfaceProvider(camera.cameraInfo))
        }, ContextCompat.getMainExecutor(this))
    }
    

Java

    private ImageCapture imageCapture;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        PreviewView previewView = findViewById(R.id.previewView);

        ListenableFuture cameraProviderFuture =
                ProcessCameraProvider.getInstance(this);

        cameraProviderFuture.addListener(() -> {
            try {
                // Camera provider is now guaranteed to be available
                ProcessCameraProvider cameraProvider = cameraProviderFuture.get();

                // Set up the view finder use case to display camera preview
                Preview preview = new Preview.Builder().build();

                // Set up the capture use case to allow users to take photos
                imageCapture = new ImageCapture.Builder()
                        .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                        .build();

                // Choose the camera by requiring a lens facing
                CameraSelector cameraSelector = new CameraSelector.Builder()
                        .requireLensFacing(lensFacing)
                        .build();

                // Attach use cases to the camera with the same lifecycle owner
                Camera camera = cameraProvider.bindToLifecycle(
                        ((LifecycleOwner) this),
                        cameraSelector,
                        preview,
                        imageCapture);

                // Connect the preview use case to the previewView
                preview.setSurfaceProvider(
                        previewView.createSurfaceProvider(camera.getCameraInfo()));
            } catch (InterruptedException | ExecutionException e) {
                // Currently no exceptions thrown. cameraProviderFuture.get() should
                // not block since the listener is being called, so no need to
                // handle InterruptedException.
            }
        }, ContextCompat.getMainExecutor(this));
    }
    

支持以下配置组合:

预览 分析 图片拍摄 组合用例
为用户提供预览、拍摄照片并分析图片流。
  拍摄照片并分析图片流。
  提供预览,并根据对流式传输的图片的分析应用了视觉效果。
  显示进入相机视野的画面并根据用户操作拍摄照片。

启用扩展后,只能保证 ImageCapturePreview。可能无法同时使用 ImageAnalysis,具体取决于 OEM 实现情况。

虽然 PreviewImageAnalysis 能够单独使用,但 ImageCapture 不能单独使用。

权限

您的应用需要 CAMERA 权限。如需将图片保存到文件中,除了所用设备要搭载 Android 10 或更高版本之外,应用还需要 WRITE_EXTERNAL_STORAGE 权限。

如需详细了解如何为应用配置权限,请参阅请求应用权限

要求

CameraX 具有以下最低版本要求:

  • Android API 级别 21
  • Android 架构组件 1.1.1

对于具有生命周期感知能力的 Activity,请使用 FragmentActivityAppCompatActivity

声明依赖项

如需添加 CameraX 依赖项,您必须将 Google Maven 代码库添加到项目中。

打开项目的 build.gradle 文件并添加 google() 代码库,如下所示:

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

将以下内容添加到 Android 代码块的末尾:

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    

将以下内容添加到应用每个模块的 build.gradle 文件中:

dependencies {
  // CameraX core library using the camera2 implementation
  def camerax_version = "1.0.0-beta08"
  // The following line is optional, as the core library is included indirectly by camera-camera2
  implementation "androidx.camera:camera-core:${camerax_version}"
  implementation "androidx.camera:camera-camera2:${camerax_version}"
  // If you want to additionally use the CameraX Lifecycle library
  implementation "androidx.camera:camera-lifecycle:${camerax_version}"
  // If you want to additionally use the CameraX View class
  implementation "androidx.camera:camera-view:1.0.0-alpha15"
  // If you want to additionally use the CameraX Extensions library
  implementation "androidx.camera:camera-extensions:1.0.0-alpha15"
}

如需详细了解如何配置应用以满足上述要求,请参阅声明依赖项

其他资源

要详细了解 CameraX,请参阅下面列出的其他资源。

Codelab

  • CameraX 使用入门
  • 代码示例

  • 官方 CameraX 示例应用