2. 预览流程实现:使用 PreviewView、绑定生命周期、实现实时预览回调

2.1 预览架构原理

CameraX 的预览流程围绕三个核心组件展开:PreviewView(显示视图)、Preview(用例配置)和 LifecycleOwner(生命周期绑定)。PreviewView 封装了 SurfaceView / TextureView,通过 SurfaceProvider 机制与 CameraX 的 Preview 用例建立连接。CameraX 监听 LifecycleOwner 的状态,自动在 ON_START 时开启预览、ON_STOP 时停止预览,无需手动管理相机生命周期。

实时预览回调是指开发者能够在每一帧预览数据送达 Surface 后获得通知,从而进行图像处理(如人脸检测、滤镜叠加)。CameraX 1.2+ 推荐使用 ImageAnalysis 用例实现帧级回调;亦可基于 Preview 的 OnPreviewOutputUpdateListener(已废弃)或自定义 SurfaceProvider 的 onSurfaceRequested 中额外创建ImageReader。

实时预览与显示的区别:Preview 仅负责将相机数据渲染到 Surface,不提供每帧回调。需要图像分析时,必须额外绑定 ImageAnalysis 用例到同一生命周期。

2.2 实现流程

  1. 添加 CameraX 依赖:在模块级 build.gradle 中引入 CameraX 库。
  2. 布局中声明 PreviewView:在 XML 中放置 androidx.camera.view.PreviewView。
  3. 获取 LifecycleOwner:Fragment / Activity 本身即为 LifecycleOwner,确保 lifecycle 可用。
  4. 创建 ProcessCameraProvider:通过 ProcessCameraProvider.getInstance(context) 获取单例实例,并在 ListenableFuture 回调中配置。
  5. 构建 Preview 用例:使用 Preview.Builder 设置分辨率、旋转等参数,调用 build() 生成对象。
  6. 设置 SurfaceProvider:从 PreviewView 获取 getSurfaceProvider() 并传递给 Preview 的 setSurfaceProvider()。
  7. 绑定生命周期:调用 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview, imageAnalysis)。
  8. 添加实时帧回调(可选):创建 ImageAnalysis 用例,设置分析器,并绑定到同一生命周期。

2.3 关键代码实现

// build.gradle (模块级)
dependencies {
    def camerax_version = "1.3.1"
    implementation "androidx.camera:camera-core:${camerax_version}"
    implementation "androidx.camera:camera-camera2:${camerax_version}"
    implementation "androidx.camera:camera-lifecycle:${camerax_version}"
    implementation "androidx.camera:camera-view:${camerax_version}"
}
// PreviewActivity.kt
class PreviewActivity : AppCompatActivity() {
    private lateinit var previewView: PreviewView
    private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_preview)
        previewView = findViewById(R.id.previewView)

        cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        cameraProviderFuture.addListener({
            val cameraProvider = cameraProviderFuture.get()
            bindPreview(cameraProvider)
        }, ContextCompat.getMainExecutor(this))
    }

    private fun bindPreview(cameraProvider: ProcessCameraProvider) {
        val preview = Preview.Builder().build()
        val surfaceProvider = previewView.surfaceProvider
        preview.setSurfaceProvider(surfaceProvider)

        // 可选:实时预览回调(ImageAnalysis)
        val imageAnalysis = ImageAnalysis.Builder()
            .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
            .build()
        imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(this)) { imageProxy ->
            // 处理每一帧(imageProxy)
            imageProxy.close()
        }

        val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
        cameraProvider.unbindAll()
        cameraProvider.bindToLifecycle(
            this, cameraSelector, preview, imageAnalysis
        )
    }
}

2.4 API 参考

API说明
ProcessCameraProvider.getInstance(context)获取 CameraX 单例,管理相机生命周期和用例绑定
Preview.Builder.build()创建预览用例,可配置分辨率、目标旋转等
PreviewView.getSurfaceProvider()返回预览视图提供的 SurfaceProvider,用于连接 Preview
Preview.setSurfaceProvider(SurfaceProvider)将 Preview 输出定向到指定 Surface
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, vararg useCases)绑定生命周期,自动管理用例的开启/停止
ImageAnalysis.Analyzer分析器接口,在每一帧上执行回调

2.5 调试与验证

  • 预览黑屏:检查 CAMERA 权限是否在运行时申请,且 AndroidManifest.xml 中声明 <uses-permission android:name="android.permission.CAMERA" />。
  • 生命周期日志:在 bindToLifecycle 前添加 Log.d(TAG, "binding preview"),并在 onStart/onStop 中确认日志。
  • SurfaceProvider 是否已设置:若 preview.setSurfaceProvider(null) 则无画面。确保在绑定前调用。
  • 分辨率适配:使用 Preview.Builder().setTargetResolution(Size(1920,1080)) 设置目标分辨率,避免默认分辨率不合预期。
常见错误:
  • cameraProvider.unbindAll() 未调用导致重复绑定抛出异常。
  • 在 Fragment 中未使用 viewLifecycleOwner 而是使用 this,导致离开 Fragment 后预览未释放。
  • 未设置 ImageAnalysis 的背压策略,导致内存堆积。

2.6 性能与稳定性优化

  • 预览尺寸选择:使用 setTargetResolution 配合 Preview.Builder,避免过大的 Surface 导致 GPU 过载。推荐 1280×720 平衡清晰度与性能。
  • 帧率控制:不进行实时帧回调时,不要创建 ImageAnalysis 用例;若需要,设置 STRATEGY_KEEP_ONLY_LATEST 避免队列积压。
  • 焦点模式:通过 CameraX 的 CameraControl 或 Preview.Builder 的扩展设置连续自动对焦。
  • 生命周期解绑:在 onDestroy 或 Fragment 视图销毁时,确保 cameraProvider.unbindAll() 已被调用(bindToLifecycle 内部已处理,但显式调用可提前释放资源)。

2.7 预览流程示意图

CameraX 预览流程(含实时回调) App 启动 获取Provider 创建Preview 关联PreviewView bindToLifecycle 预览画面 实时帧回调 UI更新 提示:实时帧回调通过 ImageAnalysis 实现,预览显示与回调可并发运行。无需分析时移除 ImageAnalysis 以节省性能。

2.8 部署注意事项

  • Android 最低版本要求:CameraX 1.3 要求 minSdkVersion ≥ 21。
  • 在 Fragment 中使用时,生命周期应绑定到 viewLifecycleOwner,而非 Fragment 自身,避免在视图重建时回调混乱。
  • 在 Android 10+ 设备上,建议测试不同相机缩放比下的预览清晰度,必要时调用 CameraControl.setZoomRatio()。
  • 若应用需要在后台保持预览(如画中画),需额外处理 CameraX 暂停恢复逻辑,通常不推荐。

2.9 常见问题排查

现象可能原因解决措施
预览黑屏权限未授予;SurfaceProvider 未设置;生命周期未绑定申请权限;检查 preview.setSurfaceProvider();确认 bindToLifecycle 调用
预览闪烁/卡顿分辨率过高;CPU 负载过高降低目标分辨率;关闭 ImageAnalysis;减少 UI 动画
实时回调不触发ImageAnalysis 未绑定;分析器阻塞确认 setAnalyzer 调用;确保回调中及时 close() 释放
App 崩溃:
“Camera is closing”
在停止生命周期后仍然调用相机使用 LifecycleObserver 或 LiveData 避免异步回调

2.10 本节小结