Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a not removed ViewTreeObserver listener cause memory leaks?

I'm listening to layout changes of a view using an OnGlobalLayoutListener:

view.getViewTreeObserver().addOnGlobalLayoutListener(myListener);

Since I'm interested in the events of this listener as long as the view exists I see no need to call removeOnGlobalLayoutListener(myListener).

Can this cause memory leaks or is the listener garbage collected along with the view? Assume that the listener holds a reference to the view.


The backgound is that I want to create a module that can be attached to certain views and does stuff based on layout changes. If removing is not necessary its creation would be just as simple as new FancyModule(theView) and the constructor then takes care of binding the listener. If removal is necessary I'd have to implement a destructor method which I'd like to prevent.

like image 966
McFarlane Avatar asked Apr 22 '16 08:04

McFarlane


3 Answers

I had the same memory leak problem, I tried to unregister OnGlobalLayoutListener in onDestroyView in the fragment but the problem still existed, then I tried to add onDetachListener for my view and then unregister OnGlobalLayoutListener and it's worked.

In kotlin I used:

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

You can use the addOnAttachStateChangeListener method too.

like image 72
ali-star Avatar answered Oct 31 '22 14:10

ali-star


Potential memory leak depends only on your architecture.

Normally, it's fine not to call removeOnGlobalLayoutListener(myListener). View holds reference to ViewTreeObserver which holds reference to added OnGlobalLayoutListener. If you don't have another reference to the listener, it's garbage collected along the view.

Now, if your implementation of OnGlobalLayoutListener holds reference to the view it is still fine. A reference cycle is not a problem for Android's garbage collector.

A problem can be created if you have another component that holds reference to the OnGlobalLayoutListener implementation. If the component lives longer than the view (e.g. it is held via the application object) then you create a memory leak of the view (and context) through the listener.

It is important to not hold the view when it's no longer used. A simple way how to avoid leaking the view is to use WeakReference.

like image 27
Tomik Avatar answered Oct 31 '22 13:10

Tomik


Yes, it can leak. Here's an example trace from LeakCanary,

  • com.xxx.Activity has leaked:
  • GC ROOT static android.view.inputmethod.InputMethodManager.sInstance
  • references android.view.inputmethod.InputMethodManager.mCurRootView
  • references com.android.internal.policy.DecorView.mAttachInfo
  • references android.view.View$AttachInfo.mTreeObserver
  • references android.view.ViewTreeObserver.mOnGlobalLayoutListeners
  • references android.view.ViewTreeObserver$CopyOnWriteArray.mData
  • references java.util.ArrayList.elementData
  • references array java.lang.Object[].[0]
  • references com.xxx.Activity$setExpandedToolbarHeight$layoutListener$1.this$0 (anonymous implementation of android.view.ViewTreeObserver$OnGlobalLayoutListener)
  • leaks com.xxx.Activity instance
like image 2
miguel Avatar answered Oct 31 '22 14:10

miguel