Android CameraX在ImageCapture后停止片段时引发异常

Android CameraX在ImageCapture后停止片段时引发异常,android,kotlin,android-lifecycle,android-camerax,Android,Kotlin,Android Lifecycle,Android Camerax,高级概述:我正在尝试捕获一张照片,并在成功捕获图像后返回上一屏幕 由于某些原因,在导航回时,会引发几个与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 c

高级概述:我正在尝试捕获一张照片,并在成功捕获图像后返回上一屏幕

由于某些原因,在导航回时,会引发几个与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$1$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$5.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$1$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$5.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)
}
类TakeAfterPictureFragment:BaseFragment(){
私有lateinit变量数据绑定:TakeAfterPictureFragmentBinding
私有lateinit变量viewModel:TakeAfterPictureViewModel
@注入
lateinit变量viewModelFactory:TakeAfterPictureViewModelFactory
覆盖创建视图(
充气器:布局充气器,容器:视图组?,
savedInstanceState:捆绑?
):查看{
数据绑定=数据绑定直到充气(
充气机,
R.layout.在图片片段之后拍摄,
集装箱,
假的
)
return dataBinding.root
}
覆盖已创建的视图(视图:视图,保存状态:捆绑?){
super.onViewCreated(视图,savedInstanceState)
注入(这个)
viewModel=ViewModelProvider(
这
viewModelFactory
).get(TakeAfterPictureViewModel::class.java)
loadBeforePicture()
checkCameraPermissions()
bindCameraCaptureButton()
bindOnPhotoCaptured()
}
私人娱乐加载预览图片(){
争论?让我们{
val beforeepictureuri:Uri=it.get(“BEFORE\u PICTURE\u PATH”)作为Uri
用(这个)滑翔
.加载(在图像之前)
.into(数据绑定。图片覆盖前)
}
dataBinding.beforePictureOverlay.imageAlpha=70
}
私人娱乐checkCameraPermissions(){
如果(allPermissionsGranted()){
viewModel.startCameraPreview(viewLifecycleOwner、数据绑定、取景器、surfaceProvider)
}否则{
ActivityCompat.requestPermissions(
Requiresponsibility()、所需权限、请求代码权限
)
}
}
private fun bindCameraCaptureButton(){
dataBinding.cameraCaptureButton.setOnClickListener{
viewModel.takeAfterPicture()
}
}
私人娱乐bindOnPhotoCaptured(){
viewModel.photoUri.observe(viewLifecycleOwner,Observer{
Toast.makeText(requireContext(),“照片捕获成功”,Toast.LENGTH\u LONG.show())
viewModel.navigateBack(屏幕导航器)
})
}
覆盖onRequestPermissionsResult(
请求代码:Int,权限:数组,GrantResult:
无阵列
) {
if(requestCode==请求\代码\权限){
如果(allPermissionsGranted()){
viewModel.startCameraPreview(viewLifecycleOwner、数据绑定、取景器、surfaceProvider)
}否则{
showPermissionsNotGrandedMessage()
viewModel.navigateBack(屏幕导航器)
}
}
}
私人娱乐节目许可证snotgrandedMessage(){
Toast.makeText(
requireContext(),
“用户未授予权限。”,
吐司长度
).show()
}
private fun allPermissionsGranted()=必需的权限。全部{
ContextCompat.checkSelfPermission(
requireContext(),它
)==已授予PackageManager.PERMISSION\u权限
}
重写onDestroy(){
super.ondestory()
}
伴星{
fun newInstance()=TakeAfterPictureFragment()
私有const val请求\代码\权限=10
private val REQUIRED_PERMISSIONS=arrayOf(Manifest.permission.CAMERA)
}
}

class takeafterPictureView模型(
私人val takePictureUseCase:takePictureUseCase,
专用val outputDirectoryProvider:PhotoOutputDirectoryProvider,
私人摄像机:伊卡梅拉,
私人val执行人:执行人
):ViewModel(){
私有val_photoUri:MutableLiveData=MutableLiveData()
val photoUri:LiveData=\u photoUri
有趣的startCameraPreview(lifecycleOwner:lifecycleOwner,surfaceProvider:Preview.surfaceProvider){
照相机,开始(
遗嘱执行人,
生命周期所有者,
表面记录器
)
}
趣事(续){
val photoFile=File(
outputDirectoryProvider.getOutputDirectory(),
简化格式(
文件名\u格式,Locale.US
).format(System.currentTimeMillis())+“.jpg”
)
_photoUri.value=takePictureUseCase.execute(
摄像机,
照片文件,
)
}
有趣的导航返回(screenNavigator:screenNavigator){
screenNavigator.navigateBack()
}
伴星{
private const val FILENAME_FORMAT=“yyyy MM dd HH MM ss SSS”
}
}
类默认摄影机(
private val cameraProviderFuture:ListenableFuture,
私有val预览配置:IPreviewConfiguration,
专用val imageCaptureConfiguration:IIImageCaptureConfiguration,
):伊卡梅拉{
private val cameraProvider:ProcessCameraProvider=cameraProviderFuture.get()
私有变量imageCapture:imageCapture=imageCaptureConfiguration.configure()
private val cameraSelector=cameraSelector.DEFAULT\u BACK\u摄像机
超越有趣的开始(
执行人:执行人,
lifeCycleOwner:lifeCycleOwner,
previewSurface:Preview.SurfaceProvider
) {
cameraProviderFuture.addListener(
可运行{
试一试{
装订预览(
生命周期所有者,
previewConfiguration.configure(previewSurface)
)
}捕获(exc:异常){
Timber.e(exc,“用例绑定失败”)
}
},遗嘱执行人
)
}
private fun bindPreview(lifeCycleOwner:lifeCycleOwner,preview:preview
    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()
    }
}