CameraX:绑定LifecycleService后Activity预览停止如何解决?
问题原因
CameraX 的 ProcessCameraProvider 同一时间只能将同一个相机实例绑定到一个 LifecycleOwner。你当前分别将相机绑定到了 Activity 和 LifecycleService 两个不同的生命周期持有者,Service 的绑定操作会直接覆盖 Activity 的绑定,导致预览停止。
解决方案
根据你的需求,有两种可行的实现方式:
方式一:共享相机用例(推荐)
将 Preview 和 ImageCapture 同时绑定到 Activity 的生命周期,后台 Service 直接调用已绑定的 ImageCapture 实例完成捕获,避免重复绑定相机。
步骤1:Activity 中同时绑定预览和捕获用例
// 全局持有 ImageCapture 实例,用于给 Service 调用 private val imageCapture = ImageCapture.Builder() .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) .build() private fun bindCamera() { processCameraProvider.await().run { try { unbindAll() // 同时绑定 Preview 和 ImageCapture 到 Activity 生命周期 bindToLifecycle( this@MainActivity, CameraSelector.DEFAULT_BACK_CAMERA, Preview.Builder().build().also { it.setSurfaceProvider(binding.camera.surfaceProvider) }, imageCapture ) // 将 ImageCapture 存入单例,供 Service 获取 CameraSharedHolder.instance.setImageCapture(imageCapture) } catch (e: Exception) { Timber.w(e) } } // 启动后台服务 ContextCompat.startForegroundService(this, Intent(this, CaptureService::class.java)) } // 单例类,用于跨组件共享 ImageCapture object CameraSharedHolder { private var imageCapture: ImageCapture? = null fun setImageCapture(capture: ImageCapture) { imageCapture = capture } fun getImageCapture(): ImageCapture? = imageCapture }
步骤2:Service 中直接调用捕获
不需要再绑定相机,直接从单例获取 ImageCapture 执行捕获操作:
class CaptureService : LifecycleService() { private val captureExecutor = ContextCompat.getMainExecutor(this) override fun onCreate() { super.onCreate() startForeground(1, createForegroundNotification()) // 启动定时捕获任务(示例用 Handler 模拟) val handler = Handler(Looper.getMainLooper()) handler.postDelayed(object : Runnable { override fun run() { takePicture() handler.postDelayed(this, 5000) // 每5秒捕获一次 } }, 5000) } private fun takePicture() { CameraSharedHolder.getImageCapture()?.let { capture -> val outputFile = File(externalCacheDir, "${System.currentTimeMillis()}.jpg") val outputOptions = ImageCapture.OutputFileOptions.Builder(outputFile).build() capture.takePicture( outputOptions, captureExecutor, object : ImageCapture.OnImageSavedCallback { override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { // 处理图片保存成功逻辑 } override fun onError(exception: ImageCaptureException) { Timber.w(exception, "捕获图片失败") } } ) } } private fun createForegroundNotification(): Notification { // 创建前台通知(必须实现,否则 Service 会被系统杀死) return NotificationCompat.Builder(this, "camera_capture_channel") .setContentTitle("后台捕获中") .setSmallIcon(R.drawable.ic_notification) .build() } }
方式二:生命周期切换绑定
如果需要在 Activity 退到后台时,由 Service 接管相机捕获,可以通过生命周期回调切换相机绑定对象:
步骤1:Activity 中处理生命周期切换
override fun onResume() { super.onResume() // 通知 Service 解绑相机,Activity 重新绑定预览+捕获 CaptureService.unbindCamera() bindCameraToActivity() } override fun onPause() { super.onPause() // Activity 退后台时解绑相机,让 Service 绑定 processCameraProvider.await().unbindAll() } private fun bindCameraToActivity() { processCameraProvider.await().run { try { unbindAll() bindToLifecycle( this@MainActivity, CameraSelector.DEFAULT_BACK_CAMERA, Preview.Builder().build().also { it.setSurfaceProvider(binding.camera.surfaceProvider) }, imageCapture ) } catch (e: Exception) { Timber.w(e) } } }
步骤2:Service 中处理相机绑定
class CaptureService : LifecycleService() { companion object { fun unbindCamera() { runCatching { ProcessCameraProvider.getInstance(this@CaptureService).get().unbindAll() } } } private val imageCapture = ImageCapture.Builder().build() override fun onCreate() { super.onCreate() startForeground(1, createForegroundNotification()) bindCameraToService() } private fun bindCameraToService() { processCameraProvider.await().run { try { unbindAll() bindToLifecycle( this@CaptureService, CameraSelector.DEFAULT_BACK_CAMERA, imageCapture ) // 启动定时捕获逻辑 } catch (e: Exception) { Timber.w(e) } } } // 省略通知创建和捕获逻辑... }
内容的提问来源于stack exchange,提问作者Pitel
相关产品推荐
相关产品推荐

