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.