问题
All my CameraX initializations reside in FragmentA
, and my goal is to navigate to FragmentB
depending on some condition that must be verified inside analyze()
.
When navigating directly from analyze()
, through Logcat I can see that FragmentB
is loaded correctly but the UI freezes on the camera preview, and unfreezes only when I navigate back to FragmentA
. I discovered that under those circumstances FragmentA
doesn't go through the rest of its lifecycle correctly, meaning that onDestroyView()
and the other methods are called only when I navigate back to it, before immediately beginning a new lifecycle; this leads to cameraExecutor.shutdown()
not being called when it's required.
Edit: I updated the code to reflect my latest attempts at finding a solution. I've added a proper callback which at least looks nicer than what I was doing before, but it still doesn't help.
Out of curiosity I've added a Button
beside CameraX PreviewView
in FragmentA
's layout, so that it calls findNavController().navigate()
. Lo and behold, clicking directly on it makes it all work as expected, but unfortunately I must do it programmatically without any user input. And if I simulate the button click by calling Button#callOnClick()
or Button#performClick()
from within the callback it doesn't work again.
class MyAnalyzer(private val callback: () -> Unit) : ImageAnalysis.Analyzer {
override fun analyze(imageProxy: ImageProxy) {
if (foo) {
imageProxy.close()
callback()
}
// do other stuff with imageProxy...
imageProxy.close()
}
}
class FragmentA : Fragment() {
// rest of the code...
private lateinit var cameraExecutor: ExecutorService
override fun onDestroyView() {
super.onDestroyView()
Log.d(TAG, "executor shutdown")
cameraExecutor.shutdown()
Log.d(TAG, "FragmentA destroyed")
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
cameraExecutor = Executors.newSingleThreadExecutor()
val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
cameraProviderFuture.addListener(Runnable {
// other CameraX code...
val imageAnalysis = ImageAnalysis.Builder()
.setTargetRotation(rotation)
.build()
.also {
it.setAnalyzer(
cameraExecutor, MyAnalyzer({
findNavController().navigate(R.id.action_fragmentA_to_fragmentB)
})
)
}
// bind to lifecycle of use cases
}, ContextCompat.getMainExecutor(requireContext()))
}
回答1:
It turns out the answer was under my nose since the beginning but I couldn't see it until a few days ago. At first I was fully convinced that there was something wrong with the camera, because it was the part of the app where the issue appeared to be originating from.
Then I realized that when navigating from FragmentA
to FragmentB
, the latter begins its lifecycle before that of the first one is completed; I began to wonder if maybe it was actually the fragment's or the Navigation Component's fault, because if that were the case the freeze would have probably been caused by the camera not being released quickly enough during the fragment swap. After some general testing I discovered that apparently that's the way all fragments (and maybe even activities) work, so I gave up the possibility.
After some more tinkering I randomly discovered that if FragmentB
was totally blank code-wise (in the Kotlin file) it worked fine, but I really couldn't understand why that was the case. After some more time it finally hit me: I had always failed to realize that it was my code's fault inside FragmentB
because I was running an innocent-looking loop which lasted for a very long time, and obviously it was being executed on the main thread freezing the UI.
I knew that you shouldn't execute slow or expensive code on the main thread but all the tutorials always mention network requests or file operations, so I never once took in consideration that even a lenghty loop could have the same side effects. Of course after wrapping the loop in a coroutine the mistery was solved.
来源:https://stackoverflow.com/questions/65834355/navigating-to-another-fragment-from-camerax-analyze-blocks-current-fragments