Activity的onDestroy / Fragment的onDestroyView设置为Null的做法

42

我正在阅读 ListFragment 的源代码,并看到以下实现:

ListAdapter mAdapter;
ListView mList;
View mEmptyView;
TextView mStandardEmptyView;
View mProgressContainer;
View mListContainer;
CharSequence mEmptyText;
boolean mListShown;

/**
 * Detach from list view.
 */
@Override
public void onDestroyView() {
    mHandler.removeCallbacks(mRequestFocus);
    mList = null;
    mListShown = false;
    mEmptyView = mProgressContainer = mListContainer = null;
    mStandardEmptyView = null;
    super.onDestroyView();
}
在此函数中,谷歌开发人员将所有在ListFragment中声明的视图字段设置为Null,并删除回调“mRequestFocus”。
在ListActivity源代码中,谷歌开发人员的实现如下:
protected ListAdapter mAdapter;
protected ListView mList;

private Handler mHandler = new Handler();


@Override
protected void onDestroy() {
    mHandler.removeCallbacks(mRequestFocus);
    super.onDestroy();
}

我注意到谷歌开发者在ListActivity的onDestroy方法中并没有像他们在ListFragment类中那样将mList设置为Null。

我的问题是:

  1. 为什么谷歌开发者没有在ListActivity的onDestroy方法中将mList设置为Null?有什么原因吗?

  2. 我们需要在Activity的onDestroy和Fragment的onDestroyView中将所有的View字段都设置为Null吗?

3. 在这两个函数中设置Null的任何最佳实践是什么?

感谢您的想法!

2个回答

72

所以Fragment和Activity之间的区别在于它们的生命周期不同。当一个Activity被销毁时,它将永远消失。但是,Fragments可能会在它们被实际销毁之前多次创建和销毁它们的视图。为了澄清,在一个Activity中:

onDestroy()
onCreate()

对于同一活动实例,将永远不会按顺序发生。 对于片段,以下内容是完全有效的:

onCreate()
onCreateView()
onDestroyView()
onCreateView()
onDestroyView()
onDestroy()

有一种情况可以看到这个问题,就是当一个Fragment进入了后退栈。它的视图将会被销毁(因为不再可见),但是实例会继续保留以便在用户按下返回键以返回到该实例时轻松恢复(此时onCreateView()将再次被调用)。

onDestroyView()之后,您可以(并且很可能应该)释放所有View引用,以允许它们被垃圾回收。在许多情况下,这是不必要的,因为如果只是在进行配置更改,onDestroy()将立即跟随,整个实例将被垃圾回收。

本质上,我认为在onDestroyView()中释放任何和所有视图引用都是一个好习惯,如果您的应用程序具有大量后退栈,则可以节省相当多的内存。


非常好的解释。我有一个问题。为什么谷歌开发人员没有在ListFragment的onDestroyView中将mAdapter设置为Null。将非UI字段(例如mAdapter)设置为Null是不必要的吗? - user3078555
1
这是一个好问题。通常情况下这并不重要,但是适配器 tend to 也会持有对 Activity 上下文的引用。我认为它遵循了方法的契约,即 onDestroyVIEW,因此它们只销毁 View 引用。如果您调用了 setListAdapter(),然后稍后调用 getListAdapter(),结果却是 null,那么这将是意外的行为。由于适配器不是一个昂贵的对象(除了在 setRetainInstance() 的情况下可能会泄漏活动),因此释放它没有太多好处。 - Kevin Coppock
我在想,在绑定之前,是否最好的方法是在onCreateView中解除绑定? - j2emanue
1
当您在onCreateView中绑定新的(sub)view实例时,这将取消绑定旧的(sub)view实例。在分配新值之前设置为null是不必要的。 - John Pang
在 Kotlin 中,这将使绑定可空,强制您在绑定的每个调用上添加 null 安全运算符。有没有什么方法可以避免这种情况,或者只需在每个调用中添加运算符即可? - the_prole
显示剩余2条评论

-3

如果不影响应用程序的逻辑,就不需要设置为null。例如:if (mList == null) ...


如果我不设置Null,会不会导致内存泄漏? - user3078555
这是不可能的,因为所有引用都在片段对象下,这意味着一旦片段对象被释放,所有视图和其他内容也会被释放。 - Volodymyr Lykhonis
1
@VladimirLichonos 这是非常可能的,特别是当一个Fragment设置了setRetainInstance(true)。如果你保留这些视图引用,你很容易泄漏一个Activity实例(因为Views会保留对Activity的引用)。 - Kevin Coppock
使用视图和setRetainInstance时,这是一个奇怪的用例。但再次强调,它基于片段的逻辑,就像我上面提到的那样。 - Volodymyr Lykhonis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接