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

已做非空判断仍崩溃:WeakReference持有的Fragment空指针问题

为什么WeakReference判断非空后仍出现空指针?

这问题我之前排查过好几次,核心坑点就是WeakReference的回收特性加上代码里的竞态条件,咱们一步步理清楚:

问题根源:WeakReference的本质与竞态条件

你用WeakReference包裹DateTimePickerDialogFragment的初衷应该是避免内存泄漏,但WeakReference的特性是:只要对象没有被任何强引用持有,GC随时会把它回收掉。

看你的代码流程:

  1. 创建WeakReference并赋值给mCurrentDialogFragment,此时只有WeakReference指向新创建的DialogFragment
  2. 你调用mCurrentDialogFragment.get()设置监听器,这时候会产生一个临时强引用,但执行完监听器设置后,这个临时引用就消失了
  3. 你做了mCurrentDialogFragment != null && mCurrentDialogFragment.get() != null的非空判断,这时候get()确实能拿到对象
  4. 但从判断结束到执行mCurrentDialogFragment.get().setArguments(bundle)的这段极短时间里,GC可能已经触发,把没有强引用的DialogFragment回收了!再次调用get()自然就返回null,导致空指针崩溃。

简单说:判断和实际调用之间存在时间窗口,GC可以在这个窗口里把对象收走,这就是典型的竞态条件问题。

解决方案:用强引用临时持有对象

解决思路很简单:在判断非空后,把WeakReference里的对象赋值给一个强引用变量,后续所有操作都用这个强引用,这样GC就不会在操作过程中回收它了。修改后的代码如下:

protected void showDateTimePickerDialog() {
    mCurrentDialogFragment = new WeakReference<AppCompatDialogFragment>(new DateTimePickerDialogFragment());
    DateTimePickerDialogFragment dialog = (DateTimePickerDialogFragment) mCurrentDialogFragment.get();
    if (dialog != null) {
        dialog.setOnDateTimeSelectedListener(
            new DateTimePickerDialogFragment.OnDateTimeSelectedListener() {
                @Override
                public void onCancelled() {
                }

                @Override
                public void onDateTimeSet(int year, int monthOfYear, int dayOfMonth,
                                        int hourOfDay, int minute) {
                    ...
                }
            });

        DateTime dt = new DateTime(mCalendarTimeLineAdapter.convertPositionToMs(
                mViewPager.getCurrentItem()));

        Pair<Boolean, SublimeOptions> optionsPair =
                TimeUtils.getDateTimePickerOptionsForDiary(mScheduleRange,
                        new DateTime(dt), mCalendarTimeLineAdapter.getCalendarTodayPosition());

        Bundle bundle = new Bundle();
        bundle.putParcelable(DateTimePickerDialogFragment.BUNDLE_KEY_DATETIME_OPTIONS, optionsPair.second);

        dialog.setArguments(bundle);
        dialog.setStyle(android.support.v4.app.DialogFragment.STYLE_NO_TITLE, 0);
        dialog.show(getSupportFragmentManager(), TAG);
    }
}

另外还有个额外建议:如果这个DialogFragment是当前页面需要主动展示的,其实可以考虑直接用强引用持有它,只要在页面的onDestroy生命周期里把引用置为null,就不会造成内存泄漏,反而能避免WeakReference带来的这类问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:14:52