2. CameraX 与 Activity 生命周期集成:各阶段相机行为
CameraX 的核心优势之一是其内建的生命周期感知能力。通过将 CameraX 绑定到 Activity(或 Fragment)的 LifecycleOwner,相机资源的创建、激活、暂停和释放由库自动管理。然而,开发者仍需在特定生命周期回调中实施正确的初始化与清理逻辑,以避免内存泄漏、预览冻结或“相机已被占用”等运行时错误。
2.1 生命周期与相机行为对应关系
下表列出了标准 Activity 生命周期回调中 CameraX 的推荐行为和潜在风险。
| 生命周期回调 | CameraX 行为 | 注意事项 |
|---|---|---|
onCreate() |
创建 ProcessCameraProvider 实例,配置 Preview、ImageCapture、ImageAnalysis 用例,但不绑定到生命周期。 |
避免在此时绑定,因为 Activity 尚未进入前台,过早绑定可能导致 Surface 无效。 |
onStart() |
可选:预打开相机硬件或预热 CameraXConfig。一般无需额外操作,CameraX 内部会在此阶段准备内部资源。 |
不要在此处重复创建 Provider,否则可能触发 CameraProvider 多次初始化。 |
onResume() |
执行绑定:通过 cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, *useCases) 将用例绑定到当前 LifecycleOwner。CameraX 自动激活相机流。此时 PreviewView 的 Surface 已可用。 |
确保 bindToLifecycle 只调用一次,或在每次 onResume 时重新绑定(若 onPause 中已解绑)。推荐在 onResume 中绑定,在 onPause 中解绑。 |
onPause() |
解绑用例:调用 cameraProvider.unbindAll() 或 cameraProvider.unbind(*useCases) 以释放相机硬件和内存资源。 |
解绑后 CameraX 会停止所有用例,关闭相机。若不解绑,当 Activity 进入后台时相机仍被占用,可能导致其他应用无法使用相机。 |
onStop() |
CameraX 内部自动将 Lifecycle 状态置为 STOPPED,所有绑定到该 LifecycleOwner 的用例被暂停。无需额外代码。 | 若在 onPause 中已解绑,此处无需重复操作。若未解绑,此处 CameraX 会安全挂起,但资源未完全释放。 |
onDestroy() |
释放 ProcessCameraProvider 引用,清理监听器。如果 Provider 由 CameraX.bindToLifecycle 管理,通常不需要手动关闭。但若有自定义 CameraXConfig,需在此处 shutdown。 |
避免持有 Camera 对象(例如 CameraX.camera)的强引用,防止 Activity 泄漏。 |
2.2 核心实现流程
推荐的绑定/解绑模式是 "Resume-Bind / Pause-Unbind"。流程图如下:
2.3 核心 API 与配置
关键 API 调用点如下:
| API | 用途 | 调用时机 |
|---|---|---|
ProcessCameraProvider.getInstance(context) |
异步获取 CameraX Provider 实例。 | onCreate 或更早(应用初始化时)。 |
cameraProvider.bindToLifecycle(lifecycleOwner, selector, useCases) |
将用例绑定到 LifecycleOwner,生命周期跟随变化。 | onResume。 |
cameraProvider.unbindAll() |
解除所有绑定,释放相机。 | onPause。 |
cameraProvider.unbind(*useCases) |
解除指定用例。 | 需动态替换用例时(如切换摄像头)。 |
2.4 典型集成代码(Kotlin + Activity)
class CameraActivity : AppCompatActivity() {
private lateinit var cameraProvider: ProcessCameraProvider
private lateinit var previewView: PreviewView
private var camera: Camera? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
previewView = findViewById(R.id.previewView)
// 1. 异步获取 CameraX Provider(仅在 onResume 前准备好即可)
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener(Runnable {
cameraProvider = cameraProviderFuture.get()
}, ContextCompat.getMainExecutor(this))
}
override fun onResume() {
super.onResume()
// 2. 确保 Provider 已就绪 & PreviewView Surface 已创建
if (::cameraProvider.isInitialized) {
bindCamera()
}
}
override fun onPause() {
super.onPause()
// 3. 解绑所有用例,释放相机硬件
if (::cameraProvider.isInitialized) {
cameraProvider.unbindAll()
camera = null
}
}
private fun bindCamera() {
val preview = Preview.Builder().build()
val imageCapture = ImageCapture.Builder().build()
// 将 Preview 与 PreviewView 的表面提供器关联
preview.surfaceProvider = previewView.surfaceProvider
camera = cameraProvider.bindToLifecycle(
this, // LifecycleOwner
CameraSelector.DEFAULT_BACK_CAMERA,
preview,
imageCapture
)
}
}
2.5 调试与验证
开启 CameraX 日志可观察生命周期状态变化:
// 在 Application 中启用详细日志
CameraX.setLogLevel(Log.VERBOSE)
运行 App 后,观察 Logcat 过滤 CameraX 或 Lifecycle 标签:
D/CameraXLifecycle: Lifecycle status changed to ON_START
D/CameraXLifecycle: Lifecycle status changed to ON_RESUME
D/CameraX: Active use case: Preview, ImageCapture
D/CameraXLifecycle: Lifecycle status changed to ON_PAUSE
D/CameraXLifecycle: Unbinding all use cases
D/CameraXLifecycle: Lifecycle status changed to ON_STOP
通过日志确认绑定与解绑的配对:每次 ON_RESUME 后应有 Active use case 输出;ON_PAUSE 后应有 Unbinding all use cases。若出现多次绑定未解绑,日志中可能显示 Camera is already bound 警告。
2.6 常见错误与避坑
错误1:在 onCreate 中过早绑定。
后果:PreviewView 的 Surface 尚未准备好,导致预览黑屏或抛出 Surface not yet available 异常。
修正:将绑定推迟到 onResume,并确保 PreviewView 已通过 View.post() 或 setOnAttachStateChangeListener 确认 Surface 可用。
错误2:忘记在 onPause 中解绑。
后果:Activity 进入后台后相机硬件被持续占用,其他应用无法访问相机,系统可能强制关闭您的应用。
修正:在 onPause 中无条件调用 cameraProvider.unbindAll()。
错误3:屏幕旋转导致重复绑定。
现象:旋转屏幕时 Activity 重建,onCreate 再次获取 Provider,onResume 再次绑定,而旧 Provider 未被释放。
修正:在 onDestroy 中调用 ProcessCameraProvider.getInstance(this).get().unbindAll()(注意线程安全),或使用 lifecycle.addObserver 统一管理。推荐在清单中配置 android:configChanges="orientation|screenSize" 避免重建(仅针对简单场景)。
2.7 性能与稳定性优化
- 延迟 Provider 初始化:在
onCreate中触发异步获取,但不要在onCreate内直接.get()阻塞主线程。 - 复用 Provider:整个 Activity 生命周期内只调用一次
ProcessCameraProvider.getInstance(),避免每次绑定都创建新实例。 - 使用 Lifecycle 观察者模式:通过
lifecycle.addObserver替代手动回调,让 CameraX 完全接管生命周期转换,减少遗漏。 - 减少不必要的 unbind/bind 次数:若只是切换前后摄像头,使用
cameraProvider.unbind(currentUseCases)后重新绑定新CameraSelector,而非unbindAll。 - 内存泄漏检测:在
onDestroy中将cameraProvider引用置为null,并确保PreviewView不再持有 Activity 的强引用(若使用 Fragment 委托生命周期)。