未删除的ViewTreeObserver侦听器是否会导致内存泄漏?

时间:2016-04-22 08:28:04

标签: android memory-leaks

我正在使用OnGlobalLayoutListener收听视图的布局更改:

view.getViewTreeObserver().addOnGlobalLayoutListener(myListener);

由于我对该侦听器的事件感兴趣,只要该视图存在,我认为无需调用removeOnGlobalLayoutListener(myListener)

这是否会导致内存泄漏,或者是否与视图一起收集了侦听器垃圾?假设侦听器持有对视图的引用。

背景是我想创建一个可以附加到某些视图的模块,并根据布局更改来完成。如果不需要删除它的创建就像new FancyModule(theView)一样简单,然后构造函数负责绑定监听器。如果需要删除,我必须实施一个我想要阻止的析构函数方法。

4 个答案:

答案 0 :(得分:4)

潜在的内存泄漏仅取决于您的架构。

通常情况下,不要拨打removeOnGlobalLayoutListener(myListener)View保留对ViewTreeObserver的引用,OnGlobalLayoutListener引用了添加的OnGlobalLayoutListener。如果您没有对侦听器的另一个引用,则会在视图中收集垃圾。

现在,如果OnGlobalLayoutListener的实现持有对视图的引用,那么它仍然没问题。对于Android的垃圾收集器,参考周期不是问题。

如果您有另一个包含对WeakReference实现的引用的组件,则可以创建问题。如果组件的寿命比视图长(例如,它是通过应用程序对象保存的),那么您将通过侦听器创建视图(和上下文)的内存泄漏。

在不再使用视图时不要保持视图很重要。如何避免泄露视图的一种简单方法是使用Source Sans Pro

答案 1 :(得分:1)

是的,它可以泄漏。以下是LeakCanary的示例跟踪,

  • com.xxx.Activity已泄露:
  • GC ROOT static android.view.inputmethod.InputMethodManager.sInstance
  • 引用android.view.inputmethod.InputMethodManager.mCurRootView
  • 引用com.android.internal.policy.DecorView.mAttachInfo
  • 引用android.view.View $ AttachInfo.mTreeObserver
  • 引用android.view.ViewTreeObserver.mOnGlobalLayoutListeners
  • 引用android.view.ViewTreeObserver $ CopyOnWriteArray.mData
  • 引用java.util.ArrayList.elementData
  • 引用数组java.lang.Object []。[0]
  • 引用com.xxx.Activity $ setExpandedToolbarHeight $ layoutListener $ 1.this $ 0(android.view.ViewTreeObserver $ OnGlobalLayoutListener的匿名实现)
  • 泄漏com.xxx.Activity instance

答案 2 :(得分:0)

我有同样的内存泄漏问题,我试图在片段的onDestroyView中注销OnGlobalLayoutListener,但是问题仍然存在,然后我尝试为视图添加onDetachListener,然后注销OnGlobalLayoutListener并成功。

在科特林我用过:

view?.doOnDetach {
    onGlobalLayoutListener?.let {
        view?.viewTreeObserver?.removeOnGlobalLayoutListener(it)
    }
    onGlobalLayoutListener = null
}

您也可以使用addOnAttachStateChangeListener方法。

答案 3 :(得分:0)

我有同样的内存泄漏问题,即使我删除了onGlobalLayoutListener中的onDetachedFromWindow,泄漏仍然发生:

override fun onAttachedToWindow() {
    super.onAttachedToWindow() 
    view.viewTreeObserver.addOnGlobalLayoutListener(myListener); 
}

override fun onDetachedFromWindow() {  
    view.viewTreeObserver.removeOnGlobalLayoutListener(myListener); 
    super.onDetachedFromWindow() 
}

然后我在类似的问题上发现了answer

由于您使用的是子视图的ViewTreeObserver,因此行为稍微复杂一些,一种可能的解决方案是将OnAttachStateChangeListener添加到您的scrollView并从那里添加/删除OnScrollChangedListener。

无论如何,关于泄漏的原因: getViewTreeObserver()在返回后不会返回相同的实例 视图已从窗口分离。呼唤 removeOnScrollChangedListener()可能不会起作用, 原始的OnScrollChangedListener仍附加到旧的 ViewTreeObserver,并因此泄漏您的上下文。

OnAttachStateChangeListener上使用view并按照建议的方式删除监听器并没有帮助,因此,解决方案是使用活动根视图的ViewTreeObserver

private val activityRootView: View = (context as Activity).window.decorView.findViewById(android.R.id.content)

override fun onAttachedToWindow() {
    super.onAttachedToWindow() 
    activityRootView.viewTreeObserver.addOnGlobalLayoutListener(myListener); 
}

override fun onDetachedFromWindow() { 
    activityRootView.viewTreeObserver.removeOnGlobalLayoutListener(myListener);
    super.onDetachedFromWindow() 
}

并且不再发生泄漏。