ConnectivityManager leaking, not sure how to resolve
Asked Answered
C

5

17

So, I have this method that let's me know if the user has an active internet connection. It works well. However, leak canary has identified a memory leak associated with the connectivityManager. I am presently not closing the connectivityManager anywhere in my code at any time that I know of.

I've tried to close the connectivityManager in onDestroy. Either that isn't an option or I don't know the code. Truth be told, I simply tried to get auto fill to tell me how to do it. No luck.

public static boolean isNetworkAvailable(Context context) {
    ConnectivityManager connectivityManager =(ConnectivityManager)  context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo activeNetworkInfo =connectivityManager.getActiveNetworkInfo();
    return activeNetworkInfo !=null && activeNetworkInfo.isConnected();
}
Chromatogram answered 2/1, 2017 at 18:8 Comment(4)
context.getSystemService can be replaced by getSystemServicePestilential
@santosh I get the following error upon making that change. Non-static method 'getSystemService(java.lang.string)' cannot be referenced from a static contextChromatogram
then your code holds good.Pestilential
use application context instead of using activity context to prevent leak. please refer to answerPestilential
P
35

Use this to prevent leak,

ConnectivityManager connectivityManager = (ConnectivityManager) context.getApplicationContext()
                .getSystemService(Context.CONNECTIVITY_SERVICE);
Pestilential answered 2/1, 2017 at 18:28 Comment(3)
Thanks, I added the code, no errors. Upon a successful test, I will accept answer.Chromatogram
would love to understand why its prevent leak / why was there a leak beforeWinther
@yotamhadas it is because somewhere in the code you might be holding dead reference of context and trying to register ConnectivityManager. In Application context can save us coz it won't die until application gets killed.and It is best practice to have ConnectivityManage and wifiManager at application level rather than activity levelPestilential
F
8

This is a bug on Android M and has been fixed on L.

The reason is that on M, ConnectivityManager holds the first instance as a static object.

When you get it first using an Activity Context, the static object will always have reference to your Activity. Using the Application Context will solve the problem.

Formulate answered 21/7, 2017 at 10:32 Comment(0)
E
7

Sharing a new answer as there is a catch:

I tried fixing the bug by instantiating ConnectivityManager using the following code in my activity:

ConnectivityManager connectivityManager = (ConnectivityManager) context.getApplicationContext()
            .getSystemService(Context.CONNECTIVITY_SERVICE);

However, this did not fix the memory leak. The problem is that even before my activity is called, some dependent library might be internally using ConnectivityManager in its code which leads to static variable of context being initialized to an activity context. The trick to fix this is to instantiate ConnectivityManager in the Application class just for the sake of it (unused).

public class MyApp extends Application {
    @Override
    public void onCreate() {
         ConnectivityManager cm = (ConnectivityManager) getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
    }
}
Exhume answered 12/5, 2019 at 15:17 Comment(1)
Didn't work for me.Fantail
A
1

As @Adi mentioned this might be a library internally using ConnectivityManager, I'm using AdMob SDK this SDK is using activity context for the initializing as they mentioned in their documentation so to fix this use Application context instead of Activity context, if you're using AdMob SDK initialize it like this below.

MobileAds.initialize(this.applicationContext)
Adder answered 23/2, 2023 at 17:55 Comment(0)
M
1

None of the above answers worked for me, I tried all of them in 1 form or another.

It turns out what was causing the issue was AdMob's initialization for banner ads MobileAds.initialize(requireContext()) which I used inside one of my fragments.

The fix was to use MobileAds.initialize(MyApplication.myAppContext)

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
}

MyApplication example:

class MyApplication : Application() {

    companion object {
        lateinit  var myAppContext: Context
    }

    override fun onCreate() {
        super.onCreate()

        myAppContext = applicationContext
    }
}
Multiangular answered 3/7, 2023 at 0:24 Comment(1)
this helped me, thanks!Christeenchristel

© 2022 - 2024 — McMap. All rights reserved.