android - 从 CameraX analyze() 导航到另一个片段会阻止当前片段的生命周期并冻结 UI
问题描述
我所有的 CameraX 初始化都驻留在FragmentA
其中,我的目标是FragmentB
根据必须在内部验证的某些条件进行导航analyze()
。
直接从 导航时analyze()
,通过 Logcat 我可以看到它FragmentB
已正确加载,但 UI 在相机预览时冻结,并且仅在我导航回FragmentA
. 我发现在这些情况下FragmentA
并没有正确地完成其生命周期的其余部分,这意味着onDestroyView()
只有在我导航回它时才调用其他方法,然后立即开始新的生命周期;这导致在cameraExecutor.shutdown()
需要时不被调用。
编辑:我更新了代码以反映我最近寻找解决方案的尝试。我添加了一个适当的回调,它至少看起来比我以前做的更好,但它仍然没有帮助。
出于好奇,我Button
在 CameraX 的布局中添加了一个旁边PreviewView
,FragmentA
以便它调用findNavController().navigate()
. 瞧,直接单击它会使一切按预期工作,但不幸的是,我必须在没有任何用户输入的情况下以编程方式进行。如果我通过调用Button#callOnClick()
或Button#performClick()
从回调中模拟按钮单击,它将不再起作用。
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()))
}
解决方案
原来答案从一开始就在我的眼皮底下,但直到几天前我才看到它。起初我完全相信相机有问题,因为问题似乎源于应用程序的一部分。
然后我意识到,当从FragmentA
to导航时FragmentB
,后者在第一个完成之前就开始了它的生命周期;我开始怀疑这是否真的是片段或导航组件的错,因为如果是这种情况,冻结可能是由于在片段交换期间相机没有足够快地释放引起的。经过一些一般测试后,我发现显然这是所有片段(甚至可能是活动)的工作方式,所以我放弃了这种可能性。
经过一番修改后,我随机发现 ifFragmentB
代码完全空白(在 Kotlin 文件中)它工作得很好,但我真的不明白为什么会这样。又过了一段时间,它终于击中了我:我一直没有意识到这是我的代码内部的错,FragmentB
因为我正在运行一个看起来很无辜的循环,持续了很长时间,显然它是在主线程上执行的冻结用户界面。
我知道你不应该在主线程上执行缓慢或昂贵的代码,但是所有的教程总是提到网络请求或文件操作,所以我从来没有考虑过即使是长循环也会有同样的副作用。当然,在将循环包装在协程中之后,谜团就解决了。
推荐阅读
- angular - 如何使用从孩子到父母的事件
- java - 无法使用 Java 中的 Apache Olingo 访问 Dynamics 365 Business Central
- python - 如何在 python 中读取所有 IOT HUB 设备 C2D 消息
- python - Dockerfile ARG 运行 python 脚本
- javascript - 在 React 中处理多个 Material UI 复选框
- javascript - 反应带有有效载荷 {"name":""} 的操作'NAVIGATE'没有被任何导航器处理
- sql - 转置列
- vue.js - 反应式或计算数组
- animation - 一种矢量化光栅动画的简单方法
- javascript - 从嵌套数组在 reactjs 中创建列表