此页面介绍 CameraX 的架构,包括其结构、如何使用 API、如何处理生命周期以及如何组合用例。
CameraX 结构
您可以使用 CameraX 通过称为用例的抽象与设备的相机进行交互。以下用例可用
- 预览:接受用于显示预览的表面,例如
PreviewView
。 - 图像分析:提供可供 CPU 访问的缓冲区进行分析,例如机器学习。
- 图像捕获:捕获并保存照片。
- 视频捕获:使用
VideoCapture
捕获视频和音频。
用例可以组合并同时激活。例如,一个应用程序可以让用户使用预览用例查看相机看到的图像,使用图像分析用例来确定照片中的人是否在微笑,并在他们微笑时包含一个图像捕获用例来拍照。
API 模型
要使用该库,您需要指定以下内容
- 所需的用例以及配置选项。
- 通过附加监听器来处理输出数据。
- 预期的流程,例如何时启用相机以及何时生成数据,方法是将用例绑定到 Android 架构生命周期。
有两种编写 CameraX 应用程序的方法:CameraController
(如果您希望使用 CameraX 的最简单方法,非常适合)或 CameraProvider
(如果您需要更多灵活性,非常适合)。
CameraController
一个 CameraController
在单个类中提供了大多数 CameraX 核心功能。它需要很少的设置代码,并且自动处理相机初始化、用例管理、目标旋转、点击对焦、捏合缩放等。扩展 CameraController
的具体类是 LifecycleCameraController
。
Kotlin
val previewView: PreviewView = viewBinding.previewView var cameraController = LifecycleCameraController(baseContext) cameraController.bindToLifecycle(this) cameraController.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA previewView.controller = cameraController
Java
PreviewView previewView = viewBinding.previewView; LifecycleCameraController cameraController = new LifecycleCameraController(baseContext); cameraController.bindToLifecycle(this); cameraController.setCameraSelector(CameraSelector.DEFAULT_BACK_CAMERA); previewView.setController(cameraController);
用于 CameraController
的默认 UseCase
是 Preview
、ImageCapture
和 ImageAnalysis
。要关闭 ImageCapture
或 ImageAnalysis
,或打开 VideoCapture
,请使用 setEnabledUseCases()
方法。
有关 CameraController
的更多用法,请参阅 二维码扫描器示例 或 CameraController
基础知识视频。
CameraProvider
一个 CameraProvider
仍然易于使用,但由于应用程序开发人员处理了更多设置,因此有更多机会自定义配置,例如在 ImageAnalysis
中启用输出图像旋转或设置输出图像格式。您还可以为相机预览使用自定义 Surface
,从而提供更大的灵活性,而在 CameraController 中,您必须使用 PreviewView
。如果您的现有 Surface
代码已经是应用程序其他部分的输入,则使用它可能很有用。
您可以使用 set()
方法配置用例,并使用 build()
方法完成配置。每个用例对象都提供一组特定于用例的 API。例如,图像捕获用例提供了一个 takePicture()
方法调用。
应用程序不是在 onResume()
和 onPause()
中放置特定的启动和停止方法调用,而是指定一个生命周期来关联相机,使用 cameraProvider.bindToLifecycle()
。然后,该生命周期会通知 CameraX 何时配置相机捕获会话并确保相机状态更改适当以匹配生命周期转换。
有关每个用例的实施步骤,请参阅 实施预览、分析图像、图像捕获 和 视频捕获
预览用例与 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.getSurfaceProvider())
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.getSurfaceProvider();
有关更多示例代码,请参阅 官方 CameraX 示例应用程序。
CameraX 生命周期
CameraX 观察生命周期以确定何时打开相机、何时创建捕获会话以及何时停止和关闭。用例 API 提供方法调用和回调以监视进度。
如 组合用例 中所述,您可以将某些用例组合绑定到单个生命周期。当您的应用程序需要支持无法组合的用例时,您可以执行以下操作之一
- 将兼容的用例组合到多个 片段 中,然后在片段之间切换
- 创建一个自定义生命周期组件并使用它手动控制相机生命周期
如果您将视图和相机用例的生命周期所有者分离(例如,如果您使用自定义生命周期或 保留片段),则必须确保通过使用 ProcessCameraProvider.unbindAll()
或单独取消绑定每个用例来确保所有用例都已从 CameraX 中取消绑定。或者,当您将用例绑定到生命周期时,您可以让 CameraX 管理打开和关闭捕获会话以及取消绑定用例。
如果您的所有相机功能都对应于单个生命周期感知组件(例如 AppCompatActivity
或 AppCompat
片段)的生命周期,则在绑定所有所需用例时使用该组件的生命周期将确保相机功能在生命周期感知组件处于活动状态时准备就绪,并在其他情况下安全地释放,不会消耗任何资源。
自定义 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.getSurfaceProvider()) }, 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<ProcessCameraProvider> 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.getSurfaceProvider()); } catch (InterruptedException | ExecutionException e) { // Currently no exceptions thrown. cameraProviderFuture.get() // shouldn't block since the listener is being called, so no need to // handle InterruptedException. } }, ContextCompat.getMainExecutor(this)); }
CameraX 允许同时使用一个 Preview
、VideoCapture
、ImageAnalysis
和 ImageCapture
的实例。此外,
- 每个用例都可以独立工作。例如,应用程序可以在不使用预览的情况下录制视频。
- 启用扩展后,仅保证
ImageCapture
和Preview
组合有效。根据 OEM 实现,可能无法添加ImageAnalysis
;无法为VideoCapture
用例启用扩展。有关详细信息,请查看 扩展参考文档。 - 根据相机功能,某些相机可能在较低分辨率模式下支持该组合,但在某些较高分辨率下可能不支持同一组合。
- 在相机硬件级别为
FULL
或更低的设备上,组合Preview
、VideoCapture
和ImageCapture
或ImageAnalysis
可能会迫使 CameraX 为Preview
和VideoCapture
复制相机的PRIV
流。这种称为流共享的复制使这些功能能够同时使用,但会增加处理需求。因此,您可能会遇到稍微更高的延迟和更低的电池续航时间。
可以从 Camera2CameraInfo
中检索 支持的硬件级别。例如,以下代码检查默认后置摄像头是否为 LEVEL_3
设备
Kotlin
@androidx.annotation.OptIn(ExperimentalCamera2Interop::class) fun isBackCameraLevel3Device(cameraProvider: ProcessCameraProvider) : Boolean { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return CameraSelector.DEFAULT_BACK_CAMERA .filter(cameraProvider.availableCameraInfos) .firstOrNull() ?.let { Camera2CameraInfo.from(it) } ?.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3 } return false }
Java
@androidx.annotation.OptIn(markerClass = ExperimentalCamera2Interop.class) Boolean isBackCameraLevel3Device(ProcessCameraProvider cameraProvider) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { List\<CameraInfo\> filteredCameraInfos = CameraSelector.DEFAULT_BACK_CAMERA .filter(cameraProvider.getAvailableCameraInfos()); if (!filteredCameraInfos.isEmpty()) { return Objects.equals( Camera2CameraInfo.from(filteredCameraInfos.get(0)).getCameraCharacteristic( CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL), CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3); } } return false; }
权限
您的应用程序将需要 CAMERA
权限。要将图像保存到文件,它还需要 WRITE_EXTERNAL_STORAGE
权限,除了在运行 Android 10 或更高版本的设备上。
有关配置应用程序权限的更多信息,请阅读 请求应用程序权限。
要求
CameraX 具有以下最低版本要求
- Android API 级别 21
- Android 架构组件 1.1.1
对于生命周期感知活动,请使用 FragmentActivity
或 AppCompatActivity
。
声明依赖项
要添加对 CameraX 的依赖项,您必须将 Google Maven 存储库 添加到您的项目中。
打开项目的 settings.gradle
文件,并添加 google()
存储库,如下所示
Groovy
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } }
Kotlin
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } }
将以下内容添加到 Android 块的末尾
Groovy
android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } // For Kotlin projects kotlinOptions { jvmTarget = "1.8" } }
Kotlin
android { compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } // For Kotlin projects kotlinOptions { jvmTarget = "1.8" } }
将以下内容添加到应用程序的每个模块的 build.gradle
文件中
Groovy
dependencies { // CameraX core library using the camera2 implementation def camerax_version = "1.5.0-alpha03" // 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 VideoCapture library implementation "androidx.camera:camera-video:${camerax_version}" // If you want to additionally use the CameraX View class implementation "androidx.camera:camera-view:${camerax_version}" // If you want to additionally add CameraX ML Kit Vision Integration implementation "androidx.camera:camera-mlkit-vision:${camerax_version}" // If you want to additionally use the CameraX Extensions library implementation "androidx.camera:camera-extensions:${camerax_version}" }
Kotlin
dependencies { // CameraX core library using the camera2 implementation val camerax_version = "1.5.0-alpha03" // 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 VideoCapture library implementation("androidx.camera:camera-video:${camerax_version}") // If you want to additionally use the CameraX View class implementation("androidx.camera:camera-view:${camerax_version}") // If you want to additionally add CameraX ML Kit Vision Integration implementation("androidx.camera:camera-mlkit-vision:${camerax_version}") // If you want to additionally use the CameraX Extensions library implementation("androidx.camera:camera-extensions:${camerax_version}") }
有关配置应用程序以符合这些要求的更多信息,请参阅 声明依赖项。
CameraX 与 Camera2 的互操作性
CameraX 基于 Camera2 构建,并且 CameraX 公开了一些方法来读取甚至写入 Camera2 实现中的属性。有关完整详细信息,请参阅 Interop 包。
有关 CameraX 如何配置 Camera2 属性的更多信息,请使用 Camera2CameraInfo
读取底层的 CameraCharacteristics
。您还可以选择通过以下两种途径之一写入底层 Camera2 属性
使用
Camera2CameraControl
,它允许您设置底层CaptureRequest
上的属性,例如自动对焦模式。使用
Camera2Interop.Extender
扩展 CameraXUseCase
。这允许您设置 CaptureRequest 上的属性,就像Camera2CameraControl
一样。它还提供了一些额外的控制,例如设置流用例以优化相机以适应您的使用场景。有关信息,请参阅使用流用例提高性能。
以下代码示例使用流用例来优化视频通话。使用Camera2CameraInfo
获取视频通话流用例是否可用。然后,使用Camera2Interop.Extender
设置底层流用例。
Kotlin
// Set underlying Camera2 stream use case to optimize for video calls. val videoCallStreamId = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL.toLong() // Check available CameraInfos to find the first one that supports // the video call stream use case. val frontCameraInfo = cameraProvider.getAvailableCameraInfos() .first { cameraInfo -> val isVideoCallStreamingSupported = Camera2CameraInfo.from(cameraInfo) .getCameraCharacteristic( CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES )?.contains(videoCallStreamId) val isFrontFacing = (cameraInfo.getLensFacing() == CameraSelector.LENS_FACING_FRONT) (isVideoCallStreamingSupported == true) && isFrontFacing } val cameraSelector = frontCameraInfo.cameraSelector // Start with a Preview Builder. val previewBuilder = Preview.Builder() .setTargetAspectRatio(screenAspectRatio) .setTargetRotation(rotation) // Use Camera2Interop.Extender to set the video call stream use case. Camera2Interop.Extender(previewBuilder).setStreamUseCase(videoCallStreamId) // Bind the Preview UseCase and the corresponding CameraSelector. val preview = previewBuilder.build() camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview)
Java
// Set underlying Camera2 stream use case to optimize for video calls. Long videoCallStreamId = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL.toLong(); // Check available CameraInfos to find the first one that supports // the video call stream use case. List<CameraInfo> cameraInfos = cameraProvider.getAvailableCameraInfos(); CameraInfo frontCameraInfo = null; for (cameraInfo in cameraInfos) { Long[] availableStreamUseCases = Camera2CameraInfo.from(cameraInfo) .getCameraCharacteristic( CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES ); boolean isVideoCallStreamingSupported = Arrays.List(availableStreamUseCases) .contains(videoCallStreamId); boolean isFrontFacing = (cameraInfo.getLensFacing() == CameraSelector.LENS_FACING_FRONT); if (isVideoCallStreamingSupported && isFrontFacing) { frontCameraInfo = cameraInfo; } } if (frontCameraInfo == null) { // Handle case where video call streaming is not supported. } CameraSelector cameraSelector = frontCameraInfo.getCameraSelector(); // Start with a Preview Builder. Preview.Builder previewBuilder = Preview.Builder() .setTargetAspectRatio(screenAspectRatio) .setTargetRotation(rotation); // Use Camera2Interop.Extender to set the video call stream use case. Camera2Interop.Extender(previewBuilder).setStreamUseCase(videoCallStreamId); // Bind the Preview UseCase and the corresponding CameraSelector. Preview preview = previewBuilder.build() Camera camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview)
其他资源
要了解有关 CameraX 的更多信息,请参阅以下其他资源。
代码实验室
代码示例