Can a not removed ViewTreeObserver listener cause memory leaks?

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

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.


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.