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

按下返回键关闭App时DetectionFragment触发NullPointerException求助

问题:Fragment销毁后后台回调触发NullPointerException

我开发了一个采用单Activity多Fragment架构的Android应用,其中包含一个DetectionFragment用于检测功能,核心代码如下:

private val PERMISSIONS_REQUIRED = arrayOf(Manifest.permission.CAMERA)
class DetectionFragment : Fragment(), ObjectDetectorHelper.DetectorListener {
    private var _binding: FragmentDetectionBinding? = null
    private val binding get() = _binding!!
    // 其余代码省略...
    override fun onResults(
        results: MutableList<Detection>?,
        inferenceTime: Long,
        imageHeight: Int,
        imageWidth: Int
    ) {
        activity?.runOnUiThread {
            binding.bottomSheetLayout.inferenceTimeVal.text =
            String.format("%d ms", inferenceTime)
            // 其余代码省略...
        }
    }
}

当在该Fragment页面按下返回键并立即关闭应用时,触发致命异常,错误栈如下:

FATAL EXCEPTION: main
Process: id.naufalfajar.go, PID: 5383
at id.naufalfajar.go.view.detection.DetectionFragment.getBinding(DetectionFragment.kt:34)
at id.naufalfajar.go.view.detection.DetectionFragment.onResults$lambda$14(DetectionFragment.kt:316)
// 其余错误栈省略...

错误指向private val binding get() = _binding!!及onResults中访问binding的代码行。推测原因是后台线程仍在运行,按下返回键后Fragment的_binding已设为null,导致触发NullPointerException。


解决方案

1. 回调中先校验Fragment状态与Binding非空

在访问Binding前,先确认Fragment是否仍处于活跃状态,同时用安全调用替代非空断言,避免直接崩溃:

override fun onResults(
    results: MutableList<Detection>?,
    inferenceTime: Long,
    imageHeight: Int,
    imageWidth: Int
) {
    activity?.runOnUiThread {
        // 校验Fragment是否已绑定视图且处于活跃状态
        if (!isAdded || _binding == null) return@runOnUiThread
        
        _binding?.bottomSheetLayout?.inferenceTimeVal?.text =
            String.format("%d ms", inferenceTime)
        // 其余代码省略...
    }
}

2. Fragment销毁时主动停止后台任务

从根源上避免无用回调,在Fragment生命周期销毁阶段停止检测任务,确保后续不再触发onResults:

private lateinit var objectDetectorHelper: ObjectDetectorHelper

override fun onDestroyView() {
    super.onDestroyView()
    // 调用检测器的停止方法终止后台任务
    objectDetectorHelper.stopDetection()
    _binding = null
}

如果你的ObjectDetectorHelper没有stopDetection方法,需要补充实现:比如取消协程作用域、中断检测线程、释放模型资源等,彻底终止回调逻辑。

3. 用生命周期感知组件绑定回调

让检测器感知Fragment的生命周期变化,仅在Fragment活跃时发送回调:
首先改造ObjectDetectorHelper,添加生命周期监听:

class ObjectDetectorHelper(
    private val lifecycleOwner: LifecycleOwner,
    // 其他初始化参数
) : DefaultLifecycleObserver {
    private var detectorListener: DetectorListener? = null

    init {
        lifecycleOwner.lifecycle.addObserver(this)
    }

    fun setListener(listener: DetectorListener) {
        this.detectorListener = listener
    }

    // 仅在Fragment处于STARTED及以上状态时发送回调
    private fun sendResults(results: MutableList<Detection>?, inferenceTime: Long) {
        if (lifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
            detectorListener?.onResults(results, inferenceTime, ...)
        }
    }

    override fun onDestroy(owner: LifecycleOwner) {
        super.onDestroy(owner)
        // 销毁时清空监听、停止任务
        detectorListener = null
        stopDetection()
        owner.lifecycle.removeObserver(this)
    }

    private fun stopDetection() {
        // 实现停止检测的逻辑
    }
}

然后在Fragment中初始化时传入自身作为生命周期所有者:

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    _binding = FragmentDetectionBinding.inflate(inflater, container, false)
    val objectDetectorHelper = ObjectDetectorHelper(this)
    objectDetectorHelper.setListener(this)
    return binding.root
}

内容的提问来源于stack exchange,提问作者Zen Ryuu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 06:27:06