title: CameraX Throws Exceptions When Stopping Fragment After ImageCapture
body: <p>High-Level Overview: I am attempting to capture a photo and return to the previous screen after a successful image capture.</p>
  <p>For some reason when navigating back several CameraX related exceptions are being thrown.</p>
  <pre><code>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)
  </code></pre>
  <p>I have tested navigating backward after only starting the preview use-case and there is no issue, so I believe the problem is related to the capture use-case specifically.</p>
  <p>I have also tried manually unbinding the use-cases before exiting the fragment although that should be unnecessary because the camera use-cases are bound to the fragment's lifecycle.</p>
  <p>I am new to working with this library and any ideas would be appreciated.</p>
  <pre><code>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(&quot;BEFORE_PICTURE_PATH&quot;) 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(), &quot;Photo capture successful.&quot;, Toast.LENGTH_LONG).show()
          viewModel.navigateBack(screenNavigator)
      })
  }
  
  override fun onRequestPermissionsResult(
      requestCode: Int, permissions: Array&lt;String&gt;, 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(),
          &quot;Permissions not granted by the user.&quot;,
          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)
  }
  </code></pre>
  <p>}</p>
  <pre><code>    class TakeAfterPictureViewModel(
      private val takePictureUseCase: TakePictureUseCase,
      private val outputDirectoryProvider: PhotoOutputDirectoryProvider,
      private val camera: ICamera,
      private val executor: Executor
      ) : ViewModel() {
  
      private val _photoUri: MutableLiveData&lt;Uri&gt; = MutableLiveData()
      val photoUri: LiveData&lt;Uri&gt; = _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()) + &quot;.jpg&quot;
          )
  
          _photoUri.value = takePictureUseCase.execute(
              camera,
              photoFile,
          )
      }
  
      fun navigateBack(screenNavigator: ScreenNavigator){
          screenNavigator.navigateBack()
      }
  
      companion object {
          private const val FILENAME_FORMAT = &quot;yyyy-MM-dd-HH-mm-ss-SSS&quot;
      }
  
  }
  
  class DefaultCamera(
      private val cameraProviderFuture: ListenableFuture&lt;ProcessCameraProvider&gt;,
      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, &quot;Use case binding failed&quot;)
                  }
              }, 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()
      }
  }
  </code></pre>