Android Fragment相机预览偶发空指针触发getRotation崩溃问题咨询
问题1:哪些场景会导致此处View相关实例为空
- 异步回调执行时Fragment已经脱离视图:比如用户点击返回键关闭Fragment、配置变更(屏幕旋转、切深色模式等)触发Fragment重建、ViewPager切换导致Fragment被回收视图,此时
cameraPreview要么被置空,要么已经脱离Window,无法获取到display实例。 startCamera调用时机过早:比如在onCreateView/onViewCreated同步调用startCamera,此时cameraPreview还未完成Attach到Window的流程,display还未完成初始化,本身就是null值。- Fragment生命周期已走到销毁阶段:回调执行时Fragment已经调用了
onDestroyView,视图已经被回收,所有View相关的引用都不可用。
问题2:局部变量
cameraProviderFuture绑定监听器的写法是否合理 这个写法语法合法,但存在两个明显隐患:
- 内存泄漏风险:
ListenableFuture会强持有绑定的监听器实例,而匿名内部类的监听器会默认持有外部Fragment的强引用,如果Fragment销毁时Future还未执行完成,会导致Fragment无法被GC回收,直到Future执行结束。 - 不符合生命周期管理要求:
cameraProvider确实需要和Fragment的视图生命周期绑定,你需要在onDestroyView回调中主动调用cameraProvider.unbindAll()释放相机资源,同时将cameraProvider置空,避免后续非法调用。
修复建议
- 所有异步回调执行时先校验Fragment状态,不符合条件直接终止逻辑,同时对display取值做兜底:
cameraProviderFuture.addListener({ // 先校验状态:已经添加、视图不为空、cameraPreview不为空 if (!isAdded || view == null || cameraPreview == null) { return@addListener } cameraProvider = cameraProviderFuture.get() cameraProvider.unbindAll() // 获取rotation之前先判空,给默认值兜底 val targetRotation = cameraPreview.display?.rotation ?: Surface.ROTATION_0 preview = Preview.Builder() .setTargetRotation(targetRotation) .build() try { bindVideoCapture() } catch (exc: Exception) { Log.e("TestCameraPytorch", "用例绑定失败", exc) } preview.setSurfaceProvider(cameraPreview.surfaceProvider) }, ContextCompat.getMainExecutor(requireContext()))
- 调整
startCamera的调用时机,推荐在onViewCreated之后的onStart回调中触发,保证View已经Attach到Window。 - 在
onDestroyView中主动释放资源,避免遗留引用:
override fun onDestroyView() { super.onDestroyView() cameraProvider?.unbindAll() cameraProvider = null cameraPreview = null }
内容的提问来源于stack exchange,提问作者El_Loco
相关产品推荐
相关产品推荐

