CONNECTIVITY_ACTION intent received twice when Wifi connected
This is the proper way to register for connectivity changes on API 21 and higher. The following code can be placed in a base activity and that way you can expect every screen in your app (that inherits from this activity) to get these callbacks.
First, create a network callback which will monitor connectivity changes.
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private val networkCallback: ConnectivityManager.NetworkCallback = object : ConnectivityManager.NetworkCallback() {
// Implement the callback methods that are relevant to the actions you want to take.
// I have implemented onAvailable for connecting and onLost for disconnecting.
override fun onAvailable(network: Network?) {
super.onAvailable(network)
}
override fun onLost(network: Network?) {
super.onLost(network)
}
}
Then, register and unregister this callback in the relevant spots.
override fun onResume() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
cm?.registerNetworkCallback(NetworkRequest.Builder().build(), networkCallback)
}
}
And unregister when appropriate.
override fun onPause() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
cm?.unregisterNetworkCallback(networkCallback)
}
}
Notice that there is a check for Build.VERSION_CODES.LOLLIPOP
. This functionality is only available in Lollipop and above. Be sure to have a plan for how to handle network status changes in Pre-Lollipop devices if you support less than API 21 in your app.
NOTE: For a recent, up-to-date answer, see this one below!
After a lot of googling and debugging, I believe this is the correct way to determine if Wifi has connected or disconnected.
The onReceive()
method in the BroadcastReceiver:
public void onReceive(final Context context, final Intent intent) {
if(intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
NetworkInfo networkInfo =
intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
if(networkInfo.isConnected()) {
// Wifi is connected
Log.d("Inetify", "Wifi is connected: " + String.valueOf(networkInfo));
}
} else if(intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
NetworkInfo networkInfo =
intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if(networkInfo.getType() == ConnectivityManager.TYPE_WIFI &&
! networkInfo.isConnected()) {
// Wifi is disconnected
Log.d("Inetify", "Wifi is disconnected: " + String.valueOf(networkInfo));
}
}
}
Together with the following receiver element in AndroidManifest.xml
<receiver android:name="ConnectivityActionReceiver"
android:enabled="true" android:label="ConnectivityActionReceiver">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
<action android:name="android.net.wifi.STATE_CHANGE"/>
</intent-filter>
</receiver>
Some explanation:
When only considering
ConnectivityManager.CONNECTIVITY_ACTION
, I always get two intents containing identical NetworkInfo instances (both getType() == TYPE_WIFI and isConnected() == true) when Wifi connects - the issue described in this question.When only using
WifiManager.NETWORK_STATE_CHANGED_ACTION
, there is no intent broadcasted when Wifi disconnects, but two intents containing different NetworkInfo instances, allowing to determine one event when Wifi is connected.
NOTE: I've received one single crash report (NPE) where the intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO)
returned null. So, even if it seems to be extremely rare to happen, it might be a good idea to add a null check.
Cheers, Torsten
If you're listening on WifiManager.NETWORK_STATE_CHANGED_ACTION
you'll receive this twice because there are 2 methods in the NetworkInfo
isConnectedOrConnecting()
isConnected()
First time isConnectedOrConnecting()
returns true
and isConnected()
false
Second time isConnectedOrConnecting()
and isConnected()
return true
Cheers