第四章 创建CameraX预览用例
4.1 原理概述
CameraX 预览(Preview)用例是连接相机硬件与屏幕显示的桥梁。其核心原理包含三个要素:
- Preview.Builder – 负责配置预览参数(分辨率、帧率、目标旋转等),生成 Preview 实例。
- SurfaceProvider – 提供显示表面(Surface),CameraX 将相机帧渲染到该表面,通常由
PreviewView或 Compose 的AndroidView包装的SurfaceView/TextureView提供。 - ProcessCameraProvider – 绑定生命周期,将 Preview 用例与 Activity 或 Fragment 的生命周期关联,自动启动/停止相机。
绑定生命周期后,CameraX 内部会为 Preview 用例分配一个 CameraSession,当 SurfaceProvider 就绪时,相机帧开始流向 Surface,用户即可在屏幕上看到实时预览画面。
🔍 关键概念
SurfaceProvider 是 CameraX 1.1.0+ 引入的接口,取代了旧的 SurfaceTexture / SurfaceHolder 方式。在 Compose 中,通常通过
SurfaceProvider 是 CameraX 1.1.0+ 引入的接口,取代了旧的 SurfaceTexture / SurfaceHolder 方式。在 Compose 中,通常通过
PreviewView 或自定义 AndroidView 的 surfaceProvider 属性获得。
⚠️ 生命周期绑定
CameraX 要求绑定 LifecycleOwner(通常是 Activity 或 Fragment)。如果使用 Compose,可以用
CameraX 要求绑定 LifecycleOwner(通常是 Activity 或 Fragment)。如果使用 Compose,可以用
LocalLifecycleOwner.current 获取。生命周期不绑定的预览用例将无法启动相机。
4.2 实现流程
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 获取 ProcessCameraProvider | 通过 ProcessCameraProvider.getInstance(context) 异步获取 |
| 2 | 创建 Preview.Builder | 配置目标分辨率、帧率范围、旋转模式等 |
| 3 | 获取 SurfaceProvider | 从 PreviewView 或自定义 Surface 获取 |
| 4 | 绑定生命并启动预览 | 调用 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) |
4.3 代码实现(Compose 环境)
以下示例展示如何在 Compose 中创建一个全屏相机预览界面,使用 PreviewView 作为 SurfaceProvider。
@Composable
fun CameraPreviewScreen() {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
// PreviewView 暴露的 SurfaceProvider
val previewView = remember { PreviewView(context) }
DisposableEffect(lifecycleOwner) {
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
// 配置 Preview 用例
val preview = Preview.Builder()
.setTargetResolution(Size(1920, 1080)) // 目标分辨率
.setTargetRotation(Surface.ROTATION_0) // 主轴旋转
.build()
// 设置 SurfaceProvider 为 PreviewView 提供的
preview.setSurfaceProvider(previewView.surfaceProvider)
// 选择后置摄像头
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
// 绑定生命周期(自动管理相机)
try {
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
preview
)
} catch (e: Exception) {
Log.e("CameraPreview", "绑定失败", e)
}
}, ContextCompat.getMainExecutor(context))
onDispose {
// 生命周期结束时不需要手动释放,CameraX 自动处理
}
}
// 将 PreviewView 放入 Compose 树
AndroidView(
factory = { previewView },
modifier = Modifier.fillMaxSize()
)
}
4.4 SurfaceProvider 的多种获取方式
| 方式 | 描述 | 适用场景 |
|---|---|---|
| PreviewView 自带 | previewView.surfaceProvider | 最推荐,省去手动创建 Surface |
| 自定义 TextureView | 通过 TextureView.surfaceTextureListener 构造 SurfaceProvider | 需要定制渲染逻辑时 |
| 自定义 SurfaceView | 通过 SurfaceHolder 构造 SurfaceProvider | 需要最低延迟的渲染 |
❗ 常见错误:SurfaceProvider 为 null
如果在绑定生命周期之前没有调用
如果在绑定生命周期之前没有调用
preview.setSurfaceProvider(),CameraX 会抛出 IllegalStateException。务必在 bindToLifecycle 之前设置。
4.5 调试与部署
- 检查预览画面:确保 Activity 或 Fragment 的布局中包含了 PreviewView 并占据可见区域。
- 日志输出:监听
CameraX内部日志(通过CameraXConfig设置最低日志级别)或使用android.util.Log输出步骤信息。 - 权限验证:AndroidManifest.xml 中必须声明
CAMERA权限,且在运行时要动态请求。 - 模拟器问题:Android 模拟器需要支持 Camera 模拟(AVD 配置中勾选 Camera 支持),否则预览黑屏。
4.6 性能与稳定性优化
| 优化项 | 建议 | 说明 |
|---|---|---|
| 分辨率选择 | 根据屏幕尺寸选择合适分辨率,避免过高导致帧率下降或内存溢出 | 通常 1280x720 或 1920x1080 足够 |
| 帧率控制 | 通过 Preview.Builder.setTargetFrameRate() 限定帧率(如 30fps) | 减少不必要的渲染开销 |
| Surface 重用 | 避免频繁创建/销毁 SurfaceProvider,通过 PreviewView 保持 Surface 稳定 | CameraX 内部会做 Surface 缓存 |
| 内存监控 | 使用 android.os.Debug 或 Profiler 检查预览时的内存占用 | 防止内存泄漏 |
4.7 预览用例创建流程
下面这张流程图总结了从初始化到预览画面显示的核心步骤:
4.8 常见错误与排查
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
| 预览黑屏 | SurfaceProvider 未设置或设置太晚;或生命周期未绑定 | 确保 preview.setSurfaceProvider() 在 bind 之前调用,且 lifecycleOwner 处于 Started 状态 |
| 闪退:IllegalStateException | 未设置 SurfaceProvider 就绑定 | 参考上述流程顺序 |
| 预览翻转或方向错误 | 目标旋转配置不正确 | 根据界面方向设置 setTargetRotation() 为 Surface.ROTATION_0/90/180/270 |
| CameraX 版本不兼容 | 使用了过旧或过新的 API | 统一使用最新稳定版(如 1.3.1)并确保 Compose 版本兼容 |
🚀 快速验证
在
在
bindToLifecycle 后,可以调用 camera.cameraInfo.torchState 或 camera.cameraInfo.exposureState 确认相机是否真正启动。若获取到非 null 的 Camera 对象,说明绑定成功。
4.9 部署到真机/模拟器
- 在
AndroidManifest.xml中添加<uses-permission android:name="android.permission.CAMERA" />及<uses-feature android:name="android.hardware.camera" />。 - 动态权限请求建议使用 Compose 的
rememberLauncherForActivityResult封装,或第三方库(如 Accompanist Permissions)。 - 模拟器预览时,请确保 AVD 的 Camera 设置为 “Webcam” 或 “Emulated”。部分模拟器不支持所有分辨率,建议先使用
Preview.Builder.setTargetResolution(Size(640,480))调试。
至此,你已经完成 CameraX 预览用例的创建、配置、绑定与显示。下一阶段将学习如何切换前后摄像头,以及对预览画面进行实时滤镜处理。