1

I have the below ConnectivityReceiver class that extends BroadcastReceiver which is used to check the internet connection. Also there is another activity SplashActivity, which implements this class for checking the internet connection. I am registering the receiver in OnCreate and unregistering it in the OnDestroy method. But still, after going to next activity, LeakCanary shows memory leak in SplashActivity.

I have instantiated LeakCanary and few methods in MyApplication class. Please find below the screenshot of the leak.

Can someone please help me in detecting the memory leak and solving this issue?

ConnectivityReceiver.java

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

public class ConnectivityReceiver extends BroadcastReceiver
{
    public static ConnectivityReceiverListener connectivityReceiverListener;
    public ConnectivityReceiver()
    {
        super();
    }

    @Override
    public void onReceive(Context context, Intent arg1)
    {
        if(arg1.getAction() != null && arg1.getAction().equalsIgnoreCase(ConnectivityManager.CONNECTIVITY_ACTION))
        {
            ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            if(cm != null)
            {
                NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
                boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();

                if (connectivityReceiverListener != null)
                    connectivityReceiverListener.onNetworkConnectionChanged(isConnected);
            }
        }
    }
    public static boolean isConnected()
    {
        ConnectivityManager cm = (ConnectivityManager) MyApplication.getInstance().getApplicationContext()
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        if(cm != null)
        {
            NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
            return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
        }
        return false;
    }
    public interface ConnectivityReceiverListener
    {
        void onNetworkConnectionChanged(boolean isConnected);
    }
}

MyApplication.java

import android.app.Application;
import com.squareup.leakcanary.LeakCanary;

public class MyApplication extends Application
{
    private static MyApplication mInstance;

    @Override
    public void onCreate()
    {
        super.onCreate();
        mInstance = this;

        if(LeakCanary.isInAnalyzerProcess(this))
            return;
        LeakCanary.install(this);
    }
    public static synchronized MyApplication getInstance()
    {
        return mInstance;
    }
    public void setConnectivityListener(ConnectivityReceiver.ConnectivityReceiverListener listener)
    {
        ConnectivityReceiver.connectivityReceiverListener = listener;
    }
}

SplashActivity.java

public class SplashActivity extends AppCompatActivity implements ConnectivityReceiver.ConnectivityReceiverListener
{
    final int REQUEST_CODE_RECOVER_PLAY_SERVICES = 1001, PERMISSION_READ_STORAGE = 0;
    RelativeLayout relativeLayout;
    IntentFilter intentFilter;
    ConnectivityReceiver connectivityReceiver;
    Bitmap thumbnail;

    @Override
    public void onCreate(Bundle icicle)
    {
        super.onCreate(icicle);
        Fabric.with(this, new Crashlytics());

        setContentView(R.layout.activity_main);

        intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        connectivityReceiver = new ConnectivityReceiver();

        registerReceiver(connectivityReceiver, intentFilter);

        if(checkInternet())
        {
             Intent mainIntent = new Intent(SplashActivity.this, WelcomeActivity.class);
             SplashActivity.this.startActivity(mainIntent);
             SplashActivity.this.finish();
        }
    }

    boolean checkInternet()
    {
        boolean isConnected = ConnectivityReceiver.isConnected();
        showSnack(isConnected);
        return isConnected;
    }
    private void showSnack(boolean isConnected)
    {
        Snackbar snackbar = Snackbar.make(relativeLayout, AppConfig.noInternet, Snackbar.LENGTH_INDEFINITE);
        if(!isConnected)
        {
            snackbar.setAction("GO OFFLINE", new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Intent mainIntent = new Intent(SplashActivity.this, WelcomeActivity.class);
    SplashActivity.this.startActivity(mainIntent);
    SplashActivity.this.finish();
                }
            });
            View sbView = snackbar.getView();
            TextView textView = sbView.findViewById(android.support.design.R.id.snackbar_text);
            textView.setTextColor(Color.WHITE);

            snackbar.setActionTextColor(getResources().getColor(R.color.colorPrimary));
            snackbar.show();
        }
        else
            snackbar.dismiss();
    }
    @Override
    public void onDestroy() {
        unregisterReceiver(connectivityReceiver);
        connectivityReceiver = null;
        super.onDestroy();
    }

    @Override
    public void onPause()
    {
        super.onPause();
    }

    @Override
    public void onResume()
    {
        super.onResume();
        MyApplication.getInstance().setConnectivityListener(this);
    }

    @Override
    public void onNetworkConnectionChanged(boolean isConnected)
    {
        showSnack(isConnected);
    }
}

enter image description here

nikhil n
  • 93
  • 2
  • 7
  • It is due to you are using static listener to hold activity. Read this for help: https://stackoverflow.com/questions/11908039/android-static-fields-and-memory-leaks – Truong Giang Dam Apr 16 '18 at 04:13
  • That's because I will be using this class object in many activity and to avoid instantiating it all the time. What is the better solution ? – nikhil n Apr 16 '18 at 04:31
  • As I saw in your SplashActivity you do the two things: 1 is instantiate **ConnectivityReceiver** and 2 is **MyApplication.getInstance().setConnectivityListener(this);** . It mean you are using **ConnectivityReceiver** and set the listener locally. I don't know why do you think it can avoid instantiate in many activity. Because when you using it on another activity, you have to do same two steps above again. – Truong Giang Dam Apr 16 '18 at 04:42
  • Instantiating **ConnectivityReceiver** in the SplashActivity is to register it for the receiver **registerReceiver(connectivityReceiver, intentFilter);** and using **MyApplication.getInstance().setConnectivityListener(this);** is for calling the listener. Can you please tell me how this code can be improved ? – nikhil n Apr 16 '18 at 04:45
  • remove static declare on listener and inject the listener via constructor. And in SplashActivity create the receiver: **connectivityReceiver = new ConnectivityReceiver(this);** . Then create new method to set the listener = null in ConnectivityReceiver. I assume that method name **removeListener()**. In SplashActivity - onStop() call connectivityReceiver.removeListener(); to cut the connection between receiver and your activity. It help you avoid leak memory. Forget code in MyApplication class – Truong Giang Dam Apr 16 '18 at 04:55
  • Okay.. I will try this and get back to you. – nikhil n Apr 16 '18 at 05:03

1 Answers1

0

you need to unregisterReceiver(connectivityReceiver); receiver in onPause() or onStop();because onDestroy(); will not called until the Activity in stack of Activities. onDestroy(); will called when you will finish the Activity and unregisterReceiver(connectivityReceiver); will be excuted.

Hitesh Sarsava
  • 666
  • 4
  • 15