You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 11:36:05