Fix Memory Leak in NextDrawListener by Properly Removing the Draw Listener
Fix Memory Leak in NextDrawListener by Properly Removing the Draw Listener
Description
This pull request addresses a memory leak reported by LeakCanary where the NextDrawListener was holding a strong reference to a View through its cached viewTreeObserver. This prevented the listener from being removed properly when the view was detached, leading to the following leak chain:
InputMethodManager.mCurRootView is leaking.
androidx.swiperefreshlayout.widget.SwipeRefreshLayout.ObjectWatcher was watching this because Fragment received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks) (MemoryLeak: 7ebbad90c9850354c61b21b1c37ccf2455b65707)
com.booking.perfsuite.internal.NextDrawListener.view (NextDrawListener.kt)
java.util.ArrayList.[1] (ArrayList.kt)
android.view.ViewTreeObserver.mOnDrawListeners (ViewTreeObserver.kt)
android.view.View$AttachInfo.mTreeObserver (View$AttachInfo.kt)
android.view.ViewRootImpl.mAttachInfo (ViewRootImpl.kt)
android.view.inputmethod.InputMethodManager.mCurRootView (InputMethodManager.kt)
android.view.inputmethod.InputMethodManager.sInstance (InputMethodManager.kt)
Root Cause
-
The
NextDrawListenerwas storing a strong reference to the view, which kept the view (and its associated resources) in memory longer than necessary. -
The listener cached the
viewTreeObserverat construction time. When the view was detached, a newViewTreeObservercould be created, making the cached observer no longer valid. As a result, the listener was not removed, causing the leak.
Changes Made
-
Lifecycle-Aware Listener Management:
- The listener is now added on view attachment using a
doOnAttachextension. - The listener is removed on view detachment using a
doOnDetachextension. This ensures that when the view is no longer in use, the listener is properly cleaned up.
- The listener is now added on view attachment using a
-
Updated Removal Logic:
- Instead of relying on the cached
viewTreeObserver, the removal now uses the currentviewTreeObserverto ensure that the listener is correctly removed even if the view’s observer has changed due to lifecycle events.
- Instead of relying on the cached
Benefits
-
Prevents Memory Leaks:
By ensuring the listener is removed when the view detaches, the view and its associated resources are no longer held in memory by the listener. -
Cleaner Lifecycle Management:
This approach aligns the listener's lifecycle with the view’s lifecycle, reducing the risk of leaks and improving overall resource management.