泄漏金丝雀,Recyclerview泄漏mAdapter

FRR*_*FRR 29 android memory-leaks leakcanary

我决定现在是时候学习如何使用Leak Canary来检测我的应用程序中的泄漏,并且像往常一样,我尝试在我的项目中实现它以真正理解如何使用该工具.实现它很容易,困难的部分是阅读工具向我扔回来的东西.我有一个滚动视图似乎在内存管理器中累积内存,因为我向上和向下滚动(即使它没有加载任何新数据)所以我认为这是一个很好的候选对象来跟踪泄漏,这是结果:

在此输入图像描述

它看起来像v7.widget.RecyclerView泄漏适配器,而不是我的应用程序.但那不可能是对的......对吗?

这是适配器的代码和使用它的类:https: //gist.github.com/feresr/a53c7b68145d6414c40ec70b3b842f1e

我开始对这个问题表示赏心悦目,因为它在两年后完全不同的应用程序上重新出现了

Bri*_*cho 37

如果适配器的寿命超过了适配器RecyclerView,则必须清除适配器引用onDestroyView:

@Override
public void onDestroyView() {
    recyclerView.setAdapter(null);
    super.onDestroyView();
}
Run Code Online (Sandbox Code Playgroud)

否则,适配器将保留对RecyclerView应该已经内存不足的引用.

如果屏幕涉及过渡动画,则实际上必须更进一步,只有在视图分离时才清除适配器:

@Override
public void onDestroyView() {
    recyclerView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
        @Override
        public void onViewAttachedToWindow(View v) {
            // no-op
        }

        @Override
        public void onViewDetachedFromWindow(View v) {
            recyclerView.setAdapter(null);
        }
    });
    super.onDestroyView();
}
Run Code Online (Sandbox Code Playgroud)

  • 这就是我最终要做的,并且确实解决了这个问题。我在其他一些 SO 问题中看到了同样的答案。我想了解为什么会这样。参考链应该是 Fragment -> Recyclerview -> Adapter。因此,如果片段停止引用 Recyclerview,则 recyclerview 及其适配器都应该被垃圾收集。 (4认同)
  • 使用`RecyclerView.setAdapter`为`RecyclerView`设置适配器时,该适配器将把`RecyclerView`注册为`AdapterDataObserver`并在内部列表中保存对它的引用.如果适配器的寿命比`RecyclerView`更长(例如,如果你在'Fragment`的`onCreate`中定义适配器就是这种情况),它将保留对应该获取GC的`RecyclerView`的引用.代替.调用`recyclerView.setAdapter(null)`使用该适配器取消注册`RecyclerView`. (4认同)

Bol*_*n95 12

我能够通过覆盖RecyclerView来解决这个问题.之所以发生这种情况,是因为RecyclerView永远不会从AdapterDataObservable中注销自己.

@Override protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    if (getAdapter() != null) {
        setAdapter(null);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 难以置信的。我们还需要打补丁吗?Google开发人员对此有何评论? (2认同)

mac*_*usz 5

首先,我正在引用此文件.

它看起来像v7.widget.RecyclerView泄漏适配器,而不是我的应用程序.但那不可能是对的......对吗?

它实际上是你的适配器正在泄漏RecyclerView(并且通过跟踪图和LeakCanary活动的标题非常清楚).但是,我不确定它是"父"RecyclerView还是HourlyViewHolder中的嵌套版,或两者兼而有之.我认为罪魁祸首是你的ViewHolders.通过使它们成为非静态内部类,您明确地为它们提供了对封闭适配器类的引用,这几乎直接将适配器与循环视图耦合在一起,因为itemView持有者中的每个内容都是RecyclerView本身.

我解决这个问题的第一个建议是通过使它们成为静态内部类来解耦你的ViewHolders和Adapter .这样他们就不会持有对适配器的引用,所以你的上下文字段对他们来说是不可访问的,这也是一件好事,因为上下文引用应该谨慎地传递和存储(也是为了避免大内存泄漏).当您需要上下文来获取字符串时,请在其他位置执行此操作,例如在适配器构造函数中,但不要将上下文存储为成员.最后,DayForecastAdapter看起来也很危险:你将它的一个相同的实例传递给每一个HourlyViewHolder,这看起来像一个bug.

我认为修复设计和解耦这些类应该摆脱这种内存泄漏