-
-
Notifications
You must be signed in to change notification settings - Fork 438
Description
✍️ Describe the bug
LeakCanary reports a memory leak in TransactionActivity caused by TabLayout$TabView holding onto a destroyed TransactionActivity context.
The leak path shows that a View$$ExternalSyntheticLambda4 posted to the main thread’s Handler keeps a reference to TabLayout, which in turn holds a ContextThemeWrapper wrapping the destroyed activity.
💣 Steps to reproduce
Integrate Chucker in any Android app with LeakCanary enabled.
Trigger any network request to populate Chucker’s transaction list.
Open Chucker’s transaction list and select any transaction to open TransactionActivity.
Press the back button to close TransactionActivity.
Wait for LeakCanary to run a heap dump.
Observe the reported leak involving TabLayout$TabView and the destroyed TransactionActivity.
🔧 Expected behavior
When TransactionActivity is destroyed, all TabLayout callbacks, listeners, and TabLayoutMediator bindings should be removed so that no view holds a reference to the destroyed activity context.
📷 LeakCanary output excerpt:
│ GC Root: System class
│
├─ android.app.ActivityThread class
│ Leaking: NO (MessageQueue↓ is not leaking and a class is never leaking)
│ ↓ static ActivityThread.sMainThreadHandler
├─ android.app.ActivityThread$H instance
│ Leaking: NO (MessageQueue↓ is not leaking)
│ ↓ Handler.mQueue
├─ android.os.MessageQueue instance
│ Leaking: NO (MessageQueue#mQuitting is false)
│ HandlerThread: "main"
│ ↓ MessageQueue[6]
│ ~~~
├─ android.os.Message instance
│ Leaking: UNKNOWN
│ Retaining 16,1 kB in 211 objects
│ Message.what = 0
│ Message.when = 1179314935 (5120 ms after heap dump)
│ Message.obj = null
│ Message.callback = instance @324729400 of android.view.
│ View$$ExternalSyntheticLambda4
│ Message.target = instance @324729480 of android.view.
│ ViewRootImpl$ViewRootHandler
│ ↓ Message.callback
│ ~~~~~~~~
├─ android.view.View$$ExternalSyntheticLambda4 instance
│ Leaking: UNKNOWN
│ Retaining 16,0 kB in 210 objects
│ ↓ View$$ExternalSyntheticLambda4.f$0
│ ~~~
├─ com.google.android.material.tabs.TabLayout$TabView instance
│ Leaking: YES (View.mContext references a destroyed activity)
│ Retaining 16,0 kB in 209 objects
│ View is part of a window view hierarchy
│ View.mAttachInfo is null (view detached)
│ View.mWindowAttachCount = 1
│ mContext instance of android.view.ContextThemeWrapper, wrapping activity
│ com.chuckerteam.chucker.internal.ui.transaction.TransactionActivity with
│ mDestroyed = true
│ ↓ View.mContext
├─ android.view.ContextThemeWrapper instance
│ Leaking: YES (TabLayout$TabView↑ is leaking and ContextThemeWrapper wraps
│ an Activity with Activity.mDestroyed true)
│ Retaining 251 B in 8 objects
│ mBase instance of com.chuckerteam.chucker.internal.ui.transaction.
│ TransactionActivity with mDestroyed = true
│ ↓ ContextWrapper.mBase
╰→ com.chuckerteam.chucker.internal.ui.transaction.TransactionActivity instance
Leaking: YES (ObjectWatcher was watching this because com.chuckerteam.
chucker.internal.ui.transaction.TransactionActivity received
Activity#onDestroy() callback and Activity#mDestroyed is true)
Retaining 5,5 kB in 195 objects
key = cd488ba1-9c57-4da6-8860-5c84ed92b0e5
watchDurationMillis = 5837
retainedDurationMillis = 836
mApplication instance of com.cwp.app.App
mBase instance of androidx.appcompat.view.ContextThemeWrapper
📱 Tech info
Device: Samsung Galaxy M13
OS: Android 14 (SDK 34)
Chucker version: 4.2.0
LeakCanary version: 2.14