CameraX 在 ImageCapture 后停止片段时抛出异常
CameraX Throws Exceptions When Stopping Fragment After ImageCapture
高级概述:我正在尝试拍摄照片,并在成功拍摄图像后 return 返回到上一个屏幕。
由于某些原因,在返回时抛出了几个与 CameraX 相关的异常。
2020-12-26 19:00:43.740 8218-8218/com.webslinger.dejavu E/TakePictureUseCase$execute: Photo capture failed: Camera is closed.
androidx.camera.core.ImageCaptureException: Camera is closed.
at androidx.camera.core.ImageCapture$ImageCaptureRequest.lambda$notifyCallbackError$ImageCapture$ImageCaptureRequest(ImageCapture.java:2232)
at androidx.camera.core.-$$Lambda$ImageCapture$ImageCaptureRequest$KlqAxzwB-08wcOFrjThjf8ncF2g.run(Unknown Source:8)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:205)
at android.app.ActivityThread.main(ActivityThread.java:6991)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:884)
Caused by: androidx.camera.core.CameraClosedException: Camera is closed.
at androidx.camera.core.ImageCapture.abortImageCaptureRequests(ImageCapture.java:809)
at androidx.camera.core.ImageCapture.onStateDetached(ImageCapture.java:805)
at androidx.camera.camera2.internal.Camera2CameraImpl.notifyStateDetachedToUseCases(Camera2CameraImpl.java:732)
at androidx.camera.camera2.internal.Camera2CameraImpl.detachUseCases(Camera2CameraImpl.java:767)
at androidx.camera.core.internal.CameraUseCaseAdapter.detachUseCases(CameraUseCaseAdapter.java:280)
at androidx.camera.lifecycle.LifecycleCamera.onStop(LifecycleCamera.java:93)
at androidx.camera.lifecycle.LifecycleCamera.suspend(LifecycleCamera.java:119)
at androidx.camera.lifecycle.LifecycleCameraRepository.suspendUseCases(LifecycleCameraRepository.java:433)
at androidx.camera.lifecycle.LifecycleCameraRepository.setInactive(LifecycleCameraRepository.java:387)
at androidx.camera.lifecycle.LifecycleCameraRepository$LifecycleCameraRepositoryObserver.onStop(LifecycleCameraRepository.java:504)
at java.lang.reflect.Method.invoke(Native Method)
at androidx.lifecycle.ClassesInfoCache$MethodReference.invokeCallback(ClassesInfoCache.java:219)
at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeMethodsForEvent(ClassesInfoCache.java:194)
at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeCallbacks(ClassesInfoCache.java:185)
at androidx.lifecycle.ReflectiveGenericLifecycleObserver.onStateChanged(ReflectiveGenericLifecycleObserver.java:37)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:354)
at androidx.lifecycle.LifecycleRegistry.backwardPass(LifecycleRegistry.java:284)
at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:302)
at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:148)
at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:134)
at androidx.fragment.app.FragmentViewLifecycleOwner.handleLifecycleEvent(FragmentViewLifecycleOwner.java:62)
at androidx.fragment.app.Fragment.performStop(Fragment.java:3166)
at androidx.fragment.app.FragmentStateManager.stop(FragmentStateManager.java:630)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:324)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2168)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2094)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1990)
at androidx.fragment.app.FragmentManager.run(FragmentManager.java:524)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:205)
at android.app.ActivityThread.main(ActivityThread.java:6991)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:884)
2020-12-26 19:00:43.745 8218-8218/com.webslinger.dejavu E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.webslinger.dejavu, PID: 8218
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:503)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:884)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:884)
Caused by: androidx.camera.core.ImageCaptureException: Camera is closed.
at androidx.camera.core.ImageCapture$ImageCaptureRequest.lambda$notifyCallbackError$ImageCapture$ImageCaptureRequest(ImageCapture.java:2232)
at androidx.camera.core.-$$Lambda$ImageCapture$ImageCaptureRequest$KlqAxzwB-08wcOFrjThjf8ncF2g.run(Unknown Source:8)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:205)
at android.app.ActivityThread.main(ActivityThread.java:6991)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:884)
Caused by: androidx.camera.core.CameraClosedException: Camera is closed.
at androidx.camera.core.ImageCapture.abortImageCaptureRequests(ImageCapture.java:809)
at androidx.camera.core.ImageCapture.onStateDetached(ImageCapture.java:805)
at androidx.camera.camera2.internal.Camera2CameraImpl.notifyStateDetachedToUseCases(Camera2CameraImpl.java:732)
at androidx.camera.camera2.internal.Camera2CameraImpl.detachUseCases(Camera2CameraImpl.java:767)
at androidx.camera.core.internal.CameraUseCaseAdapter.detachUseCases(CameraUseCaseAdapter.java:280)
at androidx.camera.lifecycle.LifecycleCamera.onStop(LifecycleCamera.java:93)
at androidx.camera.lifecycle.LifecycleCamera.suspend(LifecycleCamera.java:119)
at androidx.camera.lifecycle.LifecycleCameraRepository.suspendUseCases(LifecycleCameraRepository.java:433)
at androidx.camera.lifecycle.LifecycleCameraRepository.setInactive(LifecycleCameraRepository.java:387)
at androidx.camera.lifecycle.LifecycleCameraRepository$LifecycleCameraRepositoryObserver.onStop(LifecycleCameraRepository.java:504)
at java.lang.reflect.Method.invoke(Native Method)
at androidx.lifecycle.ClassesInfoCache$MethodReference.invokeCallback(ClassesInfoCache.java:219)
at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeMethodsForEvent(ClassesInfoCache.java:194)
at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeCallbacks(ClassesInfoCache.java:185)
at androidx.lifecycle.ReflectiveGenericLifecycleObserver.onStateChanged(ReflectiveGenericLifecycleObserver.java:37)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:354)
at androidx.lifecycle.LifecycleRegistry.backwardPass(LifecycleRegistry.java:284)
at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:302)
at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:148)
at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:134)
at androidx.fragment.app.FragmentViewLifecycleOwner.handleLifecycleEvent(FragmentViewLifecycleOwner.java:62)
at androidx.fragment.app.Fragment.performStop(Fragment.java:3166)
at androidx.fragment.app.FragmentStateManager.stop(FragmentStateManager.java:630)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:324)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2168)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2094)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1990)
at androidx.fragment.app.FragmentManager.run(FragmentManager.java:524)
我已经测试了仅在启动预览用例后向后导航并且没有问题,所以我认为问题与捕获用例有关。
我也曾尝试在退出片段之前手动取消绑定用例,尽管这应该是不必要的,因为相机用例已绑定到片段的生命周期。
我是这个库的新手,如有任何想法,我们将不胜感激。
class TakeAfterPictureFragment : BaseFragment() {
private lateinit var dataBinding: TakeAfterPictureFragmentBinding
private lateinit var viewModel: TakeAfterPictureViewModel
@Inject
lateinit var viewModelFactory: TakeAfterPictureViewModelFactory
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View{
dataBinding = DataBindingUtil.inflate(
inflater,
R.layout.take_after_picture_fragment,
container,
false
)
return dataBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
inject(this)
viewModel = ViewModelProvider(
this,
viewModelFactory
).get(TakeAfterPictureViewModel::class.java)
loadBeforePicture()
checkCameraPermissions()
bindCameraCaptureButton()
bindOnPhotoCaptured()
}
private fun loadBeforePicture() {
arguments?.let {
val beforePictureUri: Uri = it.get("BEFORE_PICTURE_PATH") as Uri
Glide.with(this)
.load(beforePictureUri)
.into(dataBinding.beforePictureOverlay)
}
dataBinding.beforePictureOverlay.imageAlpha = 70
}
private fun checkCameraPermissions() {
if (allPermissionsGranted()) {
viewModel.startCameraPreview(viewLifecycleOwner, dataBinding.viewFinder.surfaceProvider)
} else {
ActivityCompat.requestPermissions(
requireActivity(), REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS
)
}
}
private fun bindCameraCaptureButton() {
dataBinding.cameraCaptureButton.setOnClickListener {
viewModel.takeAfterPicture()
}
}
private fun bindOnPhotoCaptured(){
viewModel.photoUri.observe(viewLifecycleOwner, Observer {
Toast.makeText(requireContext(), "Photo capture successful.", Toast.LENGTH_LONG).show()
viewModel.navigateBack(screenNavigator)
})
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults:
IntArray
) {
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted()) {
viewModel.startCameraPreview(viewLifecycleOwner, dataBinding.viewFinder.surfaceProvider)
} else {
showPermissionsNotGrantedMessage()
viewModel.navigateBack(screenNavigator)
}
}
}
private fun showPermissionsNotGrantedMessage() {
Toast.makeText(
requireContext(),
"Permissions not granted by the user.",
Toast.LENGTH_SHORT
).show()
}
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(
requireContext(), it
) == PackageManager.PERMISSION_GRANTED
}
override fun onDestroy() {
super.onDestroy()
}
companion object {
fun newInstance() = TakeAfterPictureFragment()
private const val REQUEST_CODE_PERMISSIONS = 10
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
}
}
class TakeAfterPictureViewModel(
private val takePictureUseCase: TakePictureUseCase,
private val outputDirectoryProvider: PhotoOutputDirectoryProvider,
private val camera: ICamera,
private val executor: Executor
) : ViewModel() {
private val _photoUri: MutableLiveData<Uri> = MutableLiveData()
val photoUri: LiveData<Uri> = _photoUri
fun startCameraPreview(lifecycleOwner: LifecycleOwner, surfaceProvider: Preview.SurfaceProvider){
camera.start(
executor,
lifecycleOwner,
surfaceProvider
)
}
fun takeAfterPicture(){
val photoFile = File(
outputDirectoryProvider.getOutputDirectory(),
SimpleDateFormat(
FILENAME_FORMAT, Locale.US
).format(System.currentTimeMillis()) + ".jpg"
)
_photoUri.value = takePictureUseCase.execute(
camera,
photoFile,
)
}
fun navigateBack(screenNavigator: ScreenNavigator){
screenNavigator.navigateBack()
}
companion object {
private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
}
}
class DefaultCamera(
private val cameraProviderFuture: ListenableFuture<ProcessCameraProvider>,
private val previewConfiguration: IPreviewConfiguration,
private val imageCaptureConfiguration: IImageCaptureConfiguration,
) : ICamera {
private val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
private var imageCapture: ImageCapture = imageCaptureConfiguration.configure()
private val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
override fun start(
executor: Executor,
lifeCycleOwner: LifecycleOwner,
previewSurface: Preview.SurfaceProvider
) {
cameraProviderFuture.addListener(
Runnable {
try {
bindPreview(
lifeCycleOwner,
previewConfiguration.configure(previewSurface)
)
} catch (exc: Exception) {
Timber.e(exc, "Use case binding failed")
}
}, executor
)
}
private fun bindPreview(lifeCycleOwner: LifecycleOwner, preview: Preview) {
// Unbind use cases before rebinding
cameraProvider.unbindAll()
// Bind use cases to camera
cameraProvider.bindToLifecycle(
lifeCycleOwner,
cameraSelector,
preview,
imageCapture
)
}
override fun takePhoto(
outputOptions: ImageCapture.OutputFileOptions,
executor: Executor,
onImageSavedCallback: ImageCapture.OnImageSavedCallback
) {
val imageCapture = imageCapture ?: return
imageCapture.takePicture(
outputOptions,
executor,
onImageSavedCallback
)
}
override fun stop() {
cameraProvider.unbindAll()
}
}
堆栈跟踪表明图像捕获在完成之前被中止,这意味着您在接收到图像捕获结果之前正在返回。从代码来看,似乎 takeAfterPicture()
调用 takePictureUseCase.execute()
立即更新 _photoUri
的值,大概这会触发对 navigateBack()
的调用,并抛出异常你'重新观察。
DefaultCamera
的takePhoto()
方法内部调用了ImageCapture.takePicture()
并传递了一个OnImageSavedCallback
的实例,_photoUri
的值应该只在调用回调的成功方法。
高级概述:我正在尝试拍摄照片,并在成功拍摄图像后 return 返回到上一个屏幕。
由于某些原因,在返回时抛出了几个与 CameraX 相关的异常。
2020-12-26 19:00:43.740 8218-8218/com.webslinger.dejavu E/TakePictureUseCase$execute: Photo capture failed: Camera is closed.
androidx.camera.core.ImageCaptureException: Camera is closed.
at androidx.camera.core.ImageCapture$ImageCaptureRequest.lambda$notifyCallbackError$ImageCapture$ImageCaptureRequest(ImageCapture.java:2232)
at androidx.camera.core.-$$Lambda$ImageCapture$ImageCaptureRequest$KlqAxzwB-08wcOFrjThjf8ncF2g.run(Unknown Source:8)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:205)
at android.app.ActivityThread.main(ActivityThread.java:6991)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:884)
Caused by: androidx.camera.core.CameraClosedException: Camera is closed.
at androidx.camera.core.ImageCapture.abortImageCaptureRequests(ImageCapture.java:809)
at androidx.camera.core.ImageCapture.onStateDetached(ImageCapture.java:805)
at androidx.camera.camera2.internal.Camera2CameraImpl.notifyStateDetachedToUseCases(Camera2CameraImpl.java:732)
at androidx.camera.camera2.internal.Camera2CameraImpl.detachUseCases(Camera2CameraImpl.java:767)
at androidx.camera.core.internal.CameraUseCaseAdapter.detachUseCases(CameraUseCaseAdapter.java:280)
at androidx.camera.lifecycle.LifecycleCamera.onStop(LifecycleCamera.java:93)
at androidx.camera.lifecycle.LifecycleCamera.suspend(LifecycleCamera.java:119)
at androidx.camera.lifecycle.LifecycleCameraRepository.suspendUseCases(LifecycleCameraRepository.java:433)
at androidx.camera.lifecycle.LifecycleCameraRepository.setInactive(LifecycleCameraRepository.java:387)
at androidx.camera.lifecycle.LifecycleCameraRepository$LifecycleCameraRepositoryObserver.onStop(LifecycleCameraRepository.java:504)
at java.lang.reflect.Method.invoke(Native Method)
at androidx.lifecycle.ClassesInfoCache$MethodReference.invokeCallback(ClassesInfoCache.java:219)
at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeMethodsForEvent(ClassesInfoCache.java:194)
at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeCallbacks(ClassesInfoCache.java:185)
at androidx.lifecycle.ReflectiveGenericLifecycleObserver.onStateChanged(ReflectiveGenericLifecycleObserver.java:37)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:354)
at androidx.lifecycle.LifecycleRegistry.backwardPass(LifecycleRegistry.java:284)
at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:302)
at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:148)
at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:134)
at androidx.fragment.app.FragmentViewLifecycleOwner.handleLifecycleEvent(FragmentViewLifecycleOwner.java:62)
at androidx.fragment.app.Fragment.performStop(Fragment.java:3166)
at androidx.fragment.app.FragmentStateManager.stop(FragmentStateManager.java:630)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:324)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2168)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2094)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1990)
at androidx.fragment.app.FragmentManager.run(FragmentManager.java:524)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:205)
at android.app.ActivityThread.main(ActivityThread.java:6991)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:884)
2020-12-26 19:00:43.745 8218-8218/com.webslinger.dejavu E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.webslinger.dejavu, PID: 8218
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:503)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:884)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:884)
Caused by: androidx.camera.core.ImageCaptureException: Camera is closed.
at androidx.camera.core.ImageCapture$ImageCaptureRequest.lambda$notifyCallbackError$ImageCapture$ImageCaptureRequest(ImageCapture.java:2232)
at androidx.camera.core.-$$Lambda$ImageCapture$ImageCaptureRequest$KlqAxzwB-08wcOFrjThjf8ncF2g.run(Unknown Source:8)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:205)
at android.app.ActivityThread.main(ActivityThread.java:6991)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:884)
Caused by: androidx.camera.core.CameraClosedException: Camera is closed.
at androidx.camera.core.ImageCapture.abortImageCaptureRequests(ImageCapture.java:809)
at androidx.camera.core.ImageCapture.onStateDetached(ImageCapture.java:805)
at androidx.camera.camera2.internal.Camera2CameraImpl.notifyStateDetachedToUseCases(Camera2CameraImpl.java:732)
at androidx.camera.camera2.internal.Camera2CameraImpl.detachUseCases(Camera2CameraImpl.java:767)
at androidx.camera.core.internal.CameraUseCaseAdapter.detachUseCases(CameraUseCaseAdapter.java:280)
at androidx.camera.lifecycle.LifecycleCamera.onStop(LifecycleCamera.java:93)
at androidx.camera.lifecycle.LifecycleCamera.suspend(LifecycleCamera.java:119)
at androidx.camera.lifecycle.LifecycleCameraRepository.suspendUseCases(LifecycleCameraRepository.java:433)
at androidx.camera.lifecycle.LifecycleCameraRepository.setInactive(LifecycleCameraRepository.java:387)
at androidx.camera.lifecycle.LifecycleCameraRepository$LifecycleCameraRepositoryObserver.onStop(LifecycleCameraRepository.java:504)
at java.lang.reflect.Method.invoke(Native Method)
at androidx.lifecycle.ClassesInfoCache$MethodReference.invokeCallback(ClassesInfoCache.java:219)
at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeMethodsForEvent(ClassesInfoCache.java:194)
at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeCallbacks(ClassesInfoCache.java:185)
at androidx.lifecycle.ReflectiveGenericLifecycleObserver.onStateChanged(ReflectiveGenericLifecycleObserver.java:37)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:354)
at androidx.lifecycle.LifecycleRegistry.backwardPass(LifecycleRegistry.java:284)
at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:302)
at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:148)
at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:134)
at androidx.fragment.app.FragmentViewLifecycleOwner.handleLifecycleEvent(FragmentViewLifecycleOwner.java:62)
at androidx.fragment.app.Fragment.performStop(Fragment.java:3166)
at androidx.fragment.app.FragmentStateManager.stop(FragmentStateManager.java:630)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:324)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2168)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2094)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1990)
at androidx.fragment.app.FragmentManager.run(FragmentManager.java:524)
我已经测试了仅在启动预览用例后向后导航并且没有问题,所以我认为问题与捕获用例有关。
我也曾尝试在退出片段之前手动取消绑定用例,尽管这应该是不必要的,因为相机用例已绑定到片段的生命周期。
我是这个库的新手,如有任何想法,我们将不胜感激。
class TakeAfterPictureFragment : BaseFragment() {
private lateinit var dataBinding: TakeAfterPictureFragmentBinding
private lateinit var viewModel: TakeAfterPictureViewModel
@Inject
lateinit var viewModelFactory: TakeAfterPictureViewModelFactory
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View{
dataBinding = DataBindingUtil.inflate(
inflater,
R.layout.take_after_picture_fragment,
container,
false
)
return dataBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
inject(this)
viewModel = ViewModelProvider(
this,
viewModelFactory
).get(TakeAfterPictureViewModel::class.java)
loadBeforePicture()
checkCameraPermissions()
bindCameraCaptureButton()
bindOnPhotoCaptured()
}
private fun loadBeforePicture() {
arguments?.let {
val beforePictureUri: Uri = it.get("BEFORE_PICTURE_PATH") as Uri
Glide.with(this)
.load(beforePictureUri)
.into(dataBinding.beforePictureOverlay)
}
dataBinding.beforePictureOverlay.imageAlpha = 70
}
private fun checkCameraPermissions() {
if (allPermissionsGranted()) {
viewModel.startCameraPreview(viewLifecycleOwner, dataBinding.viewFinder.surfaceProvider)
} else {
ActivityCompat.requestPermissions(
requireActivity(), REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS
)
}
}
private fun bindCameraCaptureButton() {
dataBinding.cameraCaptureButton.setOnClickListener {
viewModel.takeAfterPicture()
}
}
private fun bindOnPhotoCaptured(){
viewModel.photoUri.observe(viewLifecycleOwner, Observer {
Toast.makeText(requireContext(), "Photo capture successful.", Toast.LENGTH_LONG).show()
viewModel.navigateBack(screenNavigator)
})
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults:
IntArray
) {
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted()) {
viewModel.startCameraPreview(viewLifecycleOwner, dataBinding.viewFinder.surfaceProvider)
} else {
showPermissionsNotGrantedMessage()
viewModel.navigateBack(screenNavigator)
}
}
}
private fun showPermissionsNotGrantedMessage() {
Toast.makeText(
requireContext(),
"Permissions not granted by the user.",
Toast.LENGTH_SHORT
).show()
}
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(
requireContext(), it
) == PackageManager.PERMISSION_GRANTED
}
override fun onDestroy() {
super.onDestroy()
}
companion object {
fun newInstance() = TakeAfterPictureFragment()
private const val REQUEST_CODE_PERMISSIONS = 10
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
}
}
class TakeAfterPictureViewModel(
private val takePictureUseCase: TakePictureUseCase,
private val outputDirectoryProvider: PhotoOutputDirectoryProvider,
private val camera: ICamera,
private val executor: Executor
) : ViewModel() {
private val _photoUri: MutableLiveData<Uri> = MutableLiveData()
val photoUri: LiveData<Uri> = _photoUri
fun startCameraPreview(lifecycleOwner: LifecycleOwner, surfaceProvider: Preview.SurfaceProvider){
camera.start(
executor,
lifecycleOwner,
surfaceProvider
)
}
fun takeAfterPicture(){
val photoFile = File(
outputDirectoryProvider.getOutputDirectory(),
SimpleDateFormat(
FILENAME_FORMAT, Locale.US
).format(System.currentTimeMillis()) + ".jpg"
)
_photoUri.value = takePictureUseCase.execute(
camera,
photoFile,
)
}
fun navigateBack(screenNavigator: ScreenNavigator){
screenNavigator.navigateBack()
}
companion object {
private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
}
}
class DefaultCamera(
private val cameraProviderFuture: ListenableFuture<ProcessCameraProvider>,
private val previewConfiguration: IPreviewConfiguration,
private val imageCaptureConfiguration: IImageCaptureConfiguration,
) : ICamera {
private val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
private var imageCapture: ImageCapture = imageCaptureConfiguration.configure()
private val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
override fun start(
executor: Executor,
lifeCycleOwner: LifecycleOwner,
previewSurface: Preview.SurfaceProvider
) {
cameraProviderFuture.addListener(
Runnable {
try {
bindPreview(
lifeCycleOwner,
previewConfiguration.configure(previewSurface)
)
} catch (exc: Exception) {
Timber.e(exc, "Use case binding failed")
}
}, executor
)
}
private fun bindPreview(lifeCycleOwner: LifecycleOwner, preview: Preview) {
// Unbind use cases before rebinding
cameraProvider.unbindAll()
// Bind use cases to camera
cameraProvider.bindToLifecycle(
lifeCycleOwner,
cameraSelector,
preview,
imageCapture
)
}
override fun takePhoto(
outputOptions: ImageCapture.OutputFileOptions,
executor: Executor,
onImageSavedCallback: ImageCapture.OnImageSavedCallback
) {
val imageCapture = imageCapture ?: return
imageCapture.takePicture(
outputOptions,
executor,
onImageSavedCallback
)
}
override fun stop() {
cameraProvider.unbindAll()
}
}
堆栈跟踪表明图像捕获在完成之前被中止,这意味着您在接收到图像捕获结果之前正在返回。从代码来看,似乎 takeAfterPicture()
调用 takePictureUseCase.execute()
立即更新 _photoUri
的值,大概这会触发对 navigateBack()
的调用,并抛出异常你'重新观察。
DefaultCamera
的takePhoto()
方法内部调用了ImageCapture.takePicture()
并传递了一个OnImageSavedCallback
的实例,_photoUri
的值应该只在调用回调的成功方法。