21

I'm using this code to be notified when the connection is lost in API 20 and down.

registerReceiver(getConnectivityStateBroadcastReceiver(), new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));

private class ConnectivityStateBroadcastReceiver extends BaseBroadcastReceiver {

    /**
     * @param userLoggedIn
     * @param context
     * @param intent
     */
    @Override
    protected void onReceive(Boolean userLoggedIn, Context context, Intent intent) {

        Bundle extras = intent.getExtras();
        boolean notConnected = extras.getBoolean(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);

        // DO something
    }
}

but it's not working in API 21.

How can I fix that? maybe it's got to do with ConnectivityManager.NetworkCallbak but I didn't find any example to how to use it. Thanks.

Luciano Rodríguez
  • 2,239
  • 3
  • 19
  • 32
Rotem
  • 2,306
  • 3
  • 26
  • 44

3 Answers3

26

OK, so I figure out how to do it but would appreciate confirmation that this solution is the right one.

All I did is add a call to this code in the onCreate of my application class

/**
 *
 */
@SuppressLint("NewApi")
private void registerConnectivityNetworkMonitorForAPI21AndUp() {

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        return;
    }

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

    NetworkRequest.Builder builder = new NetworkRequest.Builder();

    connectivityManager.registerNetworkCallback(
            builder.build(),
            new ConnectivityManager.NetworkCallback() {
                /**
                 * @param network
                 */
                @Override
                public void onAvailable(Network network) {

                    sendBroadcast(
                            getConnectivityIntent(false)
                    );

                }

                /**
                 * @param network
                 */
                @Override
                public void onLost(Network network) {

                    sendBroadcast(
                            getConnectivityIntent(true)
                    );

                }
            }

    );

}

 /**
 * @param noConnection
 * @return
 */
private Intent getConnectivityIntent(boolean noConnection) {

    Intent intent = new Intent();

    intent.setAction("mypackage.CONNECTIVITY_CHANGE");
    intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, noConnection);

    return intent;

}

and in the IntentFilter that already monitoring my connectivity for API 20 and less I added this

IntentFilter filter = new IntentFilter();

filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction("mypackage.CONNECTIVITY_CHANGE");

and now my already working broadcast receiver get notification about network changes in API 21 too.

Ashkan
  • 1,357
  • 4
  • 16
  • 37
Rotem
  • 2,306
  • 3
  • 26
  • 44
  • 2
    I implemented this and found that in Lolipop 5.0.1 it doesn't catch the scenario where there is Mobile Data connection only (no wifi) and the connection experiences signal loss (ie. driving into a tunnel). Forcing a disconnect on the device using Flight Mode or turning off Mobile data will trigger it. – user3188040 Jun 25 '15 at 10:49
  • Im having the same issue, poat here if you find something that woks – Fire Crow Feb 11 '18 at 06:21
  • 2
    But do keep in mind that [CONNECTIVITY_ACTION](https://developer.android.com/reference/android/net/ConnectivityManager.html#CONNECTIVITY_ACTION) is deprecated in API level 28 – Wahib Ul Haq Mar 08 '19 at 11:14
  • 3
    In some cases when switching network (e. g. from lte to wifi) onAvailable for wifi network is called earlier than onLost for lte. In this case the last broadcast will tell us about losing network. – Kiryl Tkach May 07 '19 at 09:18
  • https://www.youtube.com/playlist?list=PLrnPJCHvNZuBqr_0AS9BPXgU6gvNeai5S – Anubhav Pandey Aug 21 '19 at 21:13
  • You can simply add request for updates via Pending intent of broadcast receiver – Vlad Sep 22 '19 at 06:07
12

I'll paste my complete solution since I needed to look into many places in order to make it work:

In Application class

public void onCreate(){
...

IntentFilter filter = new IntentFilter();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        Timber.d("using registerNetworkCallback");
        createChangeConnectivityMonitor();
        filter.addAction(MY_CONNECTIVITY_CHANGE);
    } else {
        Timber.d("using old broadcast receiver");
        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
    }

    registerReceiver(new SyncOnConnectivityReceiver(), filter);
 }

  @RequiresApi(Build.VERSION_CODES.N)
   private void createChangeConnectivityMonitor() {
    final Intent intent = new Intent(MY_CONNECTIVITY_CHANGE);
    ConnectivityManager connectivityManager = (ConnectivityManager) 
  getSystemService(Context.CONNECTIVITY_SERVICE);
    if (connectivityManager != null) {
        connectivityManager.registerNetworkCallback(
               new NetworkRequest.Builder().build(), 
               new ConnectivityManager.NetworkCallback() {
                    /**
                     * @param network
                     */
                    @Override
                    public void onAvailable(Network network) {
                        Timber.d("On available network");
                        sendBroadcast(intent);
                    }

                    /**
                     * @param network
                     */
                    @Override
                    public void onLost(Network network) {
                        Timber.d("On not available network");
                        sendBroadcast(intent);
                    }
                });
    }

}

In my receiver:

public class SyncOnConnectivityReceiver extends BroadcastReceiver {

@Override
 public void onReceive(@Nullable final Context context, Intent intent) {
    Timber.d("triggering on connectivity change");
    if (context != null && isNetworkAvailable(context)) {
        Executors.newSingleThreadExecutor().submit(new Runnable() {
            @Override
            public void run() {
                syncBusData(context);
            }
        });
    }
}

private boolean isNetworkAvailable(Context context) {
    ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    if (cm != null) {
        NetworkInfo networkInfo = cm.getActiveNetworkInfo();
        return networkInfo != null && networkInfo.isConnected();
    }
    return false;
}

private void syncData(Context context) {

}
}

In AndroidManifiest.xml

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<receiver android:name="com.innofied.viapool.location.SyncOnConnectivityReceiver"/>
Khaleesi
  • 470
  • 3
  • 7
  • 2
    But do keep in mind that [CONNECTIVITY_ACTION](https://developer.android.com/reference/android/net/ConnectivityManager.html#CONNECTIVITY_ACTION) is deprecated in API level 28 – Wahib Ul Haq Mar 08 '19 at 11:14
  • But when will this callback unregistered? – StayCool Aug 05 '20 at 10:02
1

Try this if you need

  • register to the network state changes
  • consider all network interfaces
  • want to be notified if network is available for any interface
  • that code supports API>20 (you can add also legacy API support easily)

Declare the necessary variables in your class:

private static final HashMap<Network, Boolean> mNetworkStates = new HashMap<>();
private final Object mSyncNetworkState = new Object();
private Boolean mNetworkState;

Define your method which monitors the interface changes. mNetworkState shows always if at least one network interface is connected.

private void registerNetworkStateInfo(@NonNull final Context context) throws Exception {
    final ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkRequest.Builder builder = new NetworkRequest.Builder();

    if (connectivityManager != null) {
        connectivityManager.registerNetworkCallback(
                builder.build(),
                new ConnectivityManager.NetworkCallback() {
                    @Override
                    public void onAvailable(@NonNull Network network) {
                        mNetworkStates.put(network, true);
                        synchronized (mSyncNetworkState){
                            mNetworkState = true;
                        }

                        Log.d("Network state", "Network state: true");
                    }

                    @Override
                    public void onLost(@NonNull Network network) {
                        mNetworkStates.put(network, false);
                        synchronized (mSyncNetworkState){
                            mNetworkState = false;
                            Log.d("Network state", "Network state: false");

                            mNetworkStates.forEach(new BiConsumer<Network, Boolean>() {
                                @Override
                                public void accept(Network nextNetwork, Boolean state) {
                                    if (state.equals(true)) {
                                        mNetworkState= true;
                                        Log.d("Network state", "Network state: but true");
                                    }
                                }
                            });
                        }
                    }
                }
        );
    } else {
        synchronized (mSyncNetworkState){
            mNetworkState = false;
        }
        throw new Exception("Connectivity Manager is not available");
    }
}

Use your mNetworkState

synchronized (mSyncNetworkState){
    if (!mNetworkState) {
        throw new Exception("No network available");
    }
}
bdevay
  • 161
  • 1
  • 10