2012-11-16 36 views
27

我读过关于呈现用户界面的碎片的设置.setOnRetainInstance(true)可能导致内存泄漏。用户界面和内存泄漏的残留碎片

有人请解释为什么会发生这种情况?我没有在任何地方找到详细的解释。

+0

只是为了记录的话题,这里有类似的主题:http://stackoverflow.com/q/11182180/693752 – Snicolas

+0

HTTP://计算器。 com/q/11160412/693752 – Snicolas

回答

75

在带有UI的Fragment中,您经常会将一些View作为实例状态来加速访问。例如指向您的EditText的链接,因此您不必一直使用findViewById

问题是View保留对Activity上下文的引用。现在,如果您保留View,您还保留对该上下文的引用。

如果上下文仍然有效,但典型的保留情况是重新启动活动,那么这没有问题。例如,经常用于屏幕旋转。活动娱乐将创建一个新的上下文,旧的上下文旨在被垃圾收集。但现在不能垃圾收集,因为您的Fragment仍然有一个旧的参考。

以下示例说明如何不这样做

public class LeakyFragment extends Fragment { 

    private View mLeak; // retained 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setRetainInstance(true); 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     mLeak = inflater.inflate(R.layout.whatever, container, false); 
     return mLeak; 
    } 

    @Override 
    public void onDestroyView() { 
     super.onDestroyView(); 
     // not cleaning up. 
    } 
} 

为了摆脱这个问题,你需要清除onDestroyView你的UI的所有引用。一旦Fragment实例被重新使用,您将被要求在onCreateView上创建一个新的UI。在onDestroyView之后保留UI也没有意义。 Ui不会被使用。

在这个例子中的修复才刚刚改变onDestroyView

@Override 
public void onDestroyView() { 
    super.onDestroyView(); 
    mLeak = null; // now cleaning up! 
} 

而除了保持到View S变量,你显然应该不保持引用到Activity(例如,从onAttach - 干净的onDetach)或任何Context(除非它是Application上下文)。

+0

有任何想法,如何处理,这可能会导致空指针山?我有几个动画侦听器,线程,并且每个人都使用其中一个引用,它在onDestroyView中被取消。所以无论何时我使用其中一个引用,我首先必须检查null。这非常不方便。 – Tamas

+1

@Tamas Listeners,Threads,...都可以保持引用,只要它们不保留对任何引用“Activity”的引用即可。如果他们有类似的引用和活动重新使用它不会导致任何有效的,所以你必须更新它无论如何。 – zapl

+1

@Tamas示例:http://pastebin.com/8A18kMym你基本上需要传播'onAttach' /'onDetach'到任何引用上下文和'onCreateView' /'onDestroyView'的任何引用视图的东西。 – zapl

2

setRetainInstance(true)用于在Activity重新创建期间保留动态片段的实例,例如屏幕旋转或其他配置更改。这并不意味着系统会永远保留片段。

由于其他原因(例如用户完成活动(即后退))终止Activity时,Fragment应该有资格进行垃圾回收。

3

保留耦合到Activity的特定对象时要小心。

注意:虽然你可以返回任何对象,你永远不应该传递是联系在一起的活动,如可绘制对象,适配器,一个查看或者某个相关的任何其他对象与上下文。如果这样做,它会泄漏原始活动实例的所有视图和资源。 (泄漏资源意味着您的应用程序会保留它们,并且它们不能被垃圾收集,因此可能会丢失大量内存。)

http://developer.android.com/guide/topics/resources/runtime-changes.html#RetainingAnObject

-7

您可以在此改变onDestroy()并调用垃圾收集器。

@Override 
public void onDestroy() { 
    super.onDestroy(); 
    System.gc(); 
    System.gc(); 
} 
+3

有20%的机会可以工作:P 2x'System.gc()'为了安全吗?永远不要依赖'gc()' –

0

“setRetainInstance”用于在活动重新创建时维护片段的状态。 根据官方文档:如果我们使用“setRetainInstance”,片段生命周期的两个方法将不会被执行(onCreate,onDestroy)。 但是,片段中包含的视图将被重新创建,这是因为生命周期将从“onCreateView”执行。 在这些情况下,如果我们在“onSaveInstanceState”中保存了一些数据,我们应该在“onActivityCreated”中而不是在“onCreate”中请求它。

公报信息:https://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)

更多信息:https://inthecheesefactory.com/blog/fragment-state-saving-best-practices/en