Skip to content

Memory leak in TransactionActivity due to TabLayout$TabView holding destroyed Activity context #1456

@IntelliJAbhishek

Description

@IntelliJAbhishek

✍️ 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions