Open mwajeeh opened 4 years ago
I'm experiencing something similar, even with the recycleChildrenOnDetach
on the LayoutManager set to true and the setDelayMsWhenRemovingAdapterOnDetach
set to 0 for all EpoxyRecyclerViews LeakCanary is still reporting leaks on the EpoxyRecyclerView.mRecycler
┬───
│ GC Root: System class
│
├─ android.view.inputmethod.InputMethodManager class
│ Leaking: NO (InputMethodManager↓ is not leaking and a class is never leaking)
│ ↓ static InputMethodManager.sInstance
├─ android.view.inputmethod.InputMethodManager instance
│ Leaking: NO (DecorView↓ is not leaking and InputMethodManager is a singleton)
│ ↓ InputMethodManager.mCurRootView
├─ com.android.internal.policy.DecorView instance
│ Leaking: NO (EpoxyRecyclerView↓ is not leaking and View attached)
│ mContext instance of com.android.internal.policy.DecorContext, wrapping activity pt.i9.app.presentation.MainActivity with mDestroyed = false
│ Parent android.view.ViewRootImpl not a android.view.View
│ View#mParent is set
│ View#mAttachInfo is not null (view attached)
│ View.mWindowAttachCount = 1
│ ↓ DecorView.mAttachInfo
├─ android.view.View$AttachInfo instance
│ Leaking: NO (EpoxyRecyclerView↓ is not leaking)
│ ↓ View$AttachInfo.mScrollContainers
├─ java.util.ArrayList instance
│ Leaking: NO (EpoxyRecyclerView↓ is not leaking)
│ ↓ ArrayList.elementData
├─ java.lang.Object[] array
│ Leaking: NO (EpoxyRecyclerView↓ is not leaking)
│ ↓ Object[].[0]
├─ com.airbnb.epoxy.EpoxyRecyclerView instance
│ Leaking: NO (View attached)
│ mContext instance of pt.i9.app.presentation.MainActivity with mDestroyed = false
│ View.parent androidx.coordinatorlayout.widget.CoordinatorLayout attached as well
│ View#mParent is set
│ View#mAttachInfo is not null (view attached)
│ View.mID = R.id.recyclerView
│ View.mWindowAttachCount = 1
│ ↓ EpoxyRecyclerView.mRecycler
│ ~~~~~~~~~
├─ androidx.recyclerview.widget.RecyclerView$Recycler instance
│ Leaking: UNKNOWN
│ ↓ RecyclerView$Recycler.mRecyclerPool
│ ~~~~~~~~~~~~~
├─ com.airbnb.epoxy.UnboundedViewPool instance
│ Leaking: UNKNOWN
│ ↓ UnboundedViewPool.scrapHeaps
│ ~~~~~~~~~~
├─ android.util.SparseArray instance
│ Leaking: UNKNOWN
│ ↓ SparseArray.mValues
│ ~~~~~~~
├─ java.lang.Object[] array
│ Leaking: UNKNOWN
│ ↓ Object[].[6]
│ ~~~
├─ java.util.LinkedList instance
│ Leaking: UNKNOWN
│ ↓ LinkedList.first
│ ~~~~~
├─ java.util.LinkedList$Node instance
│ Leaking: UNKNOWN
│ ↓ LinkedList$Node.next
│ ~~~~
├─ java.util.LinkedList$Node instance
│ Leaking: UNKNOWN
│ ↓ LinkedList$Node.item
│ ~~~~
├─ com.airbnb.epoxy.EpoxyViewHolder instance
│ Leaking: UNKNOWN
│ ↓ EpoxyViewHolder.itemView
│ ~~~~~~~~
├─ pt.i9.app.presentation.views.swipelayout.SwipeLayout instance
│ Leaking: UNKNOWN
│ mContext instance of pt.i9.app.presentation.MainActivity with mDestroyed = false
│ View#mParent is null
│ View#mAttachInfo is null (view detached)
│ View.mID = R.id.swipeLayout
│ View.mWindowAttachCount = 20
│ ↓ SwipeLayout.mChildren
│ ~~~~~~~~~
├─ android.view.View[] array
│ Leaking: UNKNOWN
│ ↓ View[].[1]
│ ~~~
├─ androidx.constraintlayout.widget.ConstraintLayout instance
│ Leaking: YES (View detached and has parent)
│ mContext instance of pt.i9.app.presentation.MainActivity with mDestroyed = false
│ View#mParent is set
│ View#mAttachInfo is null (view detached)
│ View.mID = R.id.content
│ View.mWindowAttachCount = 20
│ ↓ ConstraintLayout.mListenerInfo
├─ android.view.View$ListenerInfo instance
│ Leaking: YES (ConstraintLayout↑ is leaking)
│ ↓ View$ListenerInfo.mOnClickListener
├─ pt.i9.app.base.presentation.models.transactions.GroupedTransactionModel$bind$1 instance
│ Leaking: YES (ConstraintLayout↑ is leaking)
│ Anonymous class implementing android.view.View$OnClickListener
│ ↓ GroupedTransactionModel$bind$1.this$0
├─ pt.i9.app.base.presentation.models.transactions.GroupedTransactionModel_ instance
│ Leaking: YES (ConstraintLayout↑ is leaking)
│ ↓ GroupedTransactionModel_.onActionClickListener
├─ pt.i9.app.dashboard.presentation.DashboardFragment$dashboardController$2$11 instance
│ Leaking: YES (ConstraintLayout↑ is leaking)
│ Anonymous subclass of kotlin.jvm.internal.Lambda
│ ↓ DashboardFragment$dashboardController$2$11.this$0
├─ pt.i9.app.dashboard.presentation.DashboardFragment$dashboardController$2 instance
│ Leaking: YES (ConstraintLayout↑ is leaking)
│ Anonymous subclass of kotlin.jvm.internal.Lambda
│ ↓ DashboardFragment$dashboardController$2.this$0
╰→ pt.i9.app.dashboard.presentation.DashboardFragment instance
Leaking: YES (ObjectWatcher was watching this because pt.i9.app.dashboard.presentation.DashboardFragment received Fragment#onDestroy() callback and Fragment#mFragmentManager is null)
key = 28528e9b-c360-4410-ab49-2889938f6847
watchDurationMillis = 6960
retainedDurationMillis = 1960
key = 3be8dc75-786e-4d60-b94c-bc7d950c1536
Using Epoxy: 3.9.0
@mwajeeh the leakcanary trace you reported links the removeAdapterRunnable
Runnable, which is post delayed for 2 seconds. I am guessing that this is just delayed for longer than LeakCanary waits. you can control this with setRemoveAdapterWhenDetachedFromWindow
and setDelayMsWhenRemovingAdapterOnDetach
Should listen to activity lifecycle and invoke
clearRemovedAdapterAndCancelRunnable()
when activity is destroyed. I got this memory leak report from LeakCanary.DisablePoolEpoxyRecyclerView
is a subclass with only one method:Using Epoxy: 3.8.0