androidkotlinmemory-leaksleakcanaryandroid-connectivitymanager

ConnectivityManager Callback Memory Leak - Android


I'm using LeakCanary and it found a memory leak in ConnectivityManager. I've never used ConnectivityManager before and it's not in my project from anything that I wrote. I'm assuming maybe a 3rd party library is using it.

How can I fix this?

┬───
│ GC Root: System class
│
├─ android.net.ConnectivityManager class
│    Leaking: NO (a class is never leaking)
│    ↓ static ConnectivityManager.sCallbackHandler
│                                 ~~~~~~~~~~~~~~~~
├─ android.net.ConnectivityManager$CallbackHandler instance
│    Leaking: UNKNOWN
│    Retaining 32 B in 1 objects
│    ↓ ConnectivityManager$CallbackHandler.this$0
│                                          ~~~~~~
├─ android.net.ConnectivityManager instance
│    Leaking: UNKNOWN
│    Retaining 516.0 kB in 8048 objects
│    mContext instance of com.company.appname.MainActivity with mDestroyed =
│    true
│    ↓ ConnectivityManager.mContext
│                          ~~~~~~~~
╰→ com.company.appname.MainActivity instance
​     Leaking: YES (ObjectWatcher was watching this because com.company.
​     appname.MainActivity received Activity#onDestroy() callback and
​     Activity#mDestroyed is true)
​     Retaining 515.9 kB in 8043 objects
​     key = fb405ad1-a78b-4e8f-8d09-c1b937e1462c
​     watchDurationMillis = 8341
​     retainedDurationMillis = 3230
​     mApplication instance of android.app.Application
​     mBase instance of androidx.appcompat.view.ContextThemeWrapper

enter image description here


Solution

  • @KevinCoppock sent me in the right direction

    The memory leak culprit was AdMob, specifically MobileAds.initialize(requireContext()) for banner ads which I used inside one of my fragments.

    Once I changed it to use MobileAds.initialize(MyApplication.myAppContext), the leak no longer appeared. Here's an example:

    1- AdMob initialization in Fragment example:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        // MobileAds.initialize(requireContext()) <---- Causes Memory Leak
    
        MobileAds.initialize(MyApplication.myAppContext) // <---- No Memory Leak
    }
    

    2- MyApplication class:

    class MyApplication : Application() {
    
        companion object {
            lateinit  var myAppContext: Context
        }
    
        override fun onCreate() {
            super.onCreate()
    
            myAppContext = applicationContext
        }
    }
    

    Side note for beginners, make sure to add MyApplication to your Manifest as in android:name=".MyApplication"