Grokking Android

Getting Down to the Nitty Gritty of Android Development

Android: Getting Notified of Connectivity Changes

By

In a previous posting I have explained how you can check the device's current connectivity status.

But this is only a temporary snapshot of the status. It might change anytime - and it probably will, given the volatility of a mobile environment. Thus you want to know whenever the connectivity state changes. Thankfully Android's ConnectivityManager allows you to register for a broadcast event that is fired whenever this happens. Let's see how to make use of this event.

What we need is a BroadcastReceiver. These are the components that listen to broadcast events. Broadcast events are events that are sent with the intention of notifying multiple receivers. Android uses these broadcasts to inform interested components of system events like application installs, mounting or removing the sd card, a low battery, the completion of the boot process and so on. A change of connectivity is on of those events sent by Android.

You can register your BroadcastReceiver within your AndroidManifest.xml file or temporarily using the Context object. For listening to connectivity changes I recommend to register your receiver programmatically. Since Activities inherit from Context you can simple call the registerReceiver(BroadcastReceiver, IntentFilter) method:


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

I'm going to publish more on how to use BroadCastReceivers properly in an upcoming tutorial and I'm going to cover there why setting the receiver in your Java code is the preferred way here.

The BroadcastReceiver implementation is pretty simple:


package com.grokkingandroid.connectivity;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

public class ConnectivityChangeReceiver 
               extends BroadcastReceiver {

   @Override
   public void onReceive(Context context, Intent intent) {
      debugIntent(intent, "grokkingandroid");
   }
   
   private void debugIntent(Intent intent, String tag) {
      Log.v(tag, "action: " + intent.getAction());
      Log.v(tag, "component: " + intent.getComponent());
      Bundle extras = intent.getExtras();
      if (extras != null) {
         for (String key: extras.keySet()) {
            Log.v(tag, "key [" + key + "]: " +
               extras.get(key));
         }
      }
      else {
         Log.v(tag, "no extras");
      }
   }

}

As you can see the method onReceive(Context, Intent) is all you need to implement. This method is called whenever the broadcast, that your BroadcastReceiver is registered for, is sent.

In this case the intent never contains any data or type information, but some interesting extras. The possible keys are documented in the ConnectivityManager API:

Key Meaning
EXTRA_EXTRA_INFO Contains additional information about the network state
EXTRA_IS_FAILOVER A boolean that indicates whether this network is used as a failover for another, previously active network
EXTRA_NETWORK_INFO The NetworkInfo object containing all information about the current state of this network type
EXTRA_NO_CONNECTIVITY A boolean that is set to true if the device has no connectivity at all
EXTRA_OTHER_NETWORK_INFO The NetworkInfo object containing all information about the current state of a possible alternative network type
EXTRA_REASON The reason of the connectivity change

So let's see this in action. The following log samples show some common cases where connectivity changes occur and how they are logged.

At first my phone is connected with both 3G and WIFI enable. Now when disabling WIFI the following events are logged:


06-04 12:39:14.652 V/grokkingandroid( 7603): action: android.net.conn.CONNECTIVITY_CHANGE
06-04 12:39:14.652 V/grokkingandroid( 7603): component: ComponentInfo{com.grokkingandroid.connectivity/com.grokkingandroid.connectivity.ConnectivityChangeReceiver}
06-04 12:39:14.652 V/grokkingandroid( 7603): key [networkInfo]: NetworkInfo: type: WIFI[], state: DISCONNECTED/DISCONNECTED, reason: (unspecified), extra: (none), roaming: false, failover: false, isAvailable: false
06-04 12:39:14.652 V/grokkingandroid( 7603): key [otherNetwork]: NetworkInfo: type: mobile[HSDPA], state: CONNECTING/CONNECTING, reason: apnSwitched, extra: internet, roaming: false, failover: true, isAvailable: true
(...)
06-04 12:39:16.702 V/grokkingandroid( 7603): action: android.net.conn.CONNECTIVITY_CHANGE
06-04 12:39:16.702 V/grokkingandroid( 7603): component: ComponentInfo{com.grokkingandroid.connectivity/com.grokkingandroid.connectivity.ConnectivityChangeReceiver}
06-04 12:39:16.712 V/grokkingandroid( 7603): key [networkInfo]: NetworkInfo: type: mobile[HSDPA], state: CONNECTED/CONNECTED, reason: apnSwitched, extra: internet, roaming: false, failover: false, isAvailable: true
06-04 12:39:16.712 V/grokkingandroid( 7603): key [reason]: apnSwitched
06-04 12:39:16.712 V/grokkingandroid( 7603): key [isFailover]: true
06-04 12:39:16.712 V/grokkingandroid( 7603): key [extraInfo]: internet

As you can see there is a short lapse between two notifications. After the first notification the 3G connection was in the "CONNECTING" state - that is, it had to contact the provider, get an IP address and so on. After a short while the connection was established and another notification has been sent: Now the state has changed to "CONNECTED".

When I also disable 3G, this gets logged:


06-04 12:42:25.082 V/grokkingandroid( 7603): action: android.net.conn.CONNECTIVITY_CHANGE
06-04 12:42:25.082 V/grokkingandroid( 7603): component: ComponentInfo{com.grokkingandroid.connectivity/com.grokkingandroid.connectivity.ConnectivityChangeReceiver}
06-04 12:42:25.082 V/grokkingandroid( 7603): key [networkInfo]: NetworkInfo: type: mobile[HSDPA], state: DISCONNECTED/DISCONNECTED, reason: dataDisabled, extra: internet, roaming: false, failover: false, isAvailable: true
06-04 12:42:25.082 V/grokkingandroid( 7603): key [reason]: dataDisabled
06-04 12:42:25.082 V/grokkingandroid( 7603): key [extraInfo]: internet
06-04 12:42:25.082 V/grokkingandroid( 7603): key [noConnectivity]: true

This time no NetworkInfo for the key "otherNetwork" has been logged, because there is no alternative connectivity method to switch to.

As a final example I switch WIFI back on:


06-04 12:44:52.702 V/grokkingandroid( 7603): action: android.net.conn.CONNECTIVITY_CHANGE
06-04 12:44:52.702 V/grokkingandroid( 7603): component: ComponentInfo{com.grokkingandroid.connectivity/com.grokkingandroid.connectivity.ConnectivityChangeReceiver}
06-04 12:44:52.702 V/grokkingandroid( 7603): key [networkInfo]: NetworkInfo: type: WIFI[], state: CONNECTED/CONNECTED, reason: (unspecified), extra: (none), roaming: false, failover: false, isAvailable: true

So you see, you can gain a lot of insights. But keep in mind that on phones typically tons of connectivity changes occur. So you should use this Receiver only if you really need to know about a change in connectivity.

Wolfram Rittmeyer lives in Germany and has been developing with Java for many years.

He has been interested in Android for quite a while and has been blogging about all kind of topics around Android.

You can find him on Google+ and Twitter.