本页面介绍了 CameraX 的架构,包括其结构、如何使用 API、如何使用生命周期以及如何组合用例。
CameraX 结构
您可以使用 CameraX 通过一种称为“用例”的抽象层与设备的相机进行交互。以下用例可用:
- 预览:接受用于显示预览的 Surface,例如
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
的更多用法,请参阅 QR 码扫描器示例或 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 提供方法调用和回调来监控进度。
如组合用例中所述,您可以将某些混合用例绑定到单个生命周期。当您的应用需要支持无法组合的用例时,您可以执行以下操作之一:
- 将兼容的用例组合到多个fragment中,然后进行 fragment 切换
- 创建自定义生命周期组件并使用它手动控制相机生命周期
如果您将视图和相机用例的生命周期所有者解耦(例如,如果您使用自定义生命周期或保留 fragment),则必须通过使用 ProcessCameraProvider.unbindAll()
或单独解绑每个用例来确保所有用例都已从 CameraX 解绑。或者,当您将用例绑定到生命周期时,您可以让 CameraX 管理捕获会话的打开和关闭以及用例的解绑。
如果您所有的相机功能都对应于单个生命周期感知组件(例如 AppCompatActivity
或 AppCompat
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.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 复制相机的PRIV
流以用于Preview
和VideoCapture
。这种复制(称为流共享)可以同时使用这些功能,但会增加处理需求。因此,您可能会遇到稍高的延迟和更短的电池续航时间。
可以从 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
对于生命周期感知型 Activity,请使用 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-beta01" // 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-beta01" // 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 实现中读取甚至写入属性的方法。有关完整详细信息,请参阅互操作性软件包。
有关 CameraX 如何配置 Camera2 属性的更多信息,请使用 Camera2CameraInfo
读取底层的 CameraCharacteristics
。您还可以选择通过以下两种途径之一写入底层的 Camera2 属性:
使用
Camera2CameraControl
,它允许您设置底层CaptureRequest
上的属性,例如自动对焦模式。使用
Camera2Interop.Extender
扩展 CameraXUseCase
。这允许您像使用Camera2CameraControl
一样设置 CaptureRequest 上的属性。它还为您提供了一些额外的控制,例如设置流用例以优化相机以适应您的使用场景。有关信息,请参阅使用流用例以获得更好的性能。
以下代码示例使用流用例来优化视频通话。使用 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 的更多信息,请查阅以下其他资源。
Codelab
代码示例