Using Broadcastreceiver to get network state change - android

This is my first time using broadcast-receivers, and i thought it would be a little more straight forward than this. I have a class looking like this:
public class NetworkChangeReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo wifi = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
NetworkInfo mobile = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
boolean isConnected = wifi != null && wifi.isConnectedOrConnecting() || mobile != null && mobile.isConnectedOrConnecting();
if (isConnected) {
Log.d("Network Available ", "YES" + getResultCode());
}else{
Log.d("Network Available ", "NO" + getResultCode());
}
}
}
and i've registered it in my application like this
mReceiver = new NetworkChangeReceiver();
registerReceiver(mReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
And this is working OK, when i shut off my wifi, i get "NO" in log.d.. But how do i get something to "happen"? I want a Return-value or something which i can work with, for example if isConnected is false, then restart activity or something.
I've googled for days trying to understand what they are and how they work... Please help!

Well, the method is of type void, so a return value is out of the question. And besides, the calling class would be BroadcastManager or LocalBroadcastManager, so you wouldn't have any access to a potential return value.
You could register custom listeners inside your BroadcastReceiver, but that would mean keeping unnecessary references.
What I would do in your situation is fire off another broadcast with a custom "action" String in the "NO" block:
else{
Log.d("Network Available ", "NO" + getResultCode());
LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent(YOUR_CUSTOM_ACTION_NAME));
}
Register a listener for that event wherever needed and handle the behavior there.

Related

Race condition when detecting connectivity status change in Android

I need to monitor and determine connectivity status changes in my Android app. For that, I have registered my class as a broadcast receiver:
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
context.registerReceiver(this, filter);
Now, for determining the connectivity status, you can do the following:
#Override
public void onReceive(Context context, Intent intent) {
if (!ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
return;
}
NetworkInfo aNetworkInfo =
intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (aNetworkInfo == null) {
return;
}
boolean isConnected = aNetworkInfo.isConnected();
int networkType = aNetworkInfo.getType();
// by using isConnected & networkType, get the new connectivity status..
}
The issue is that ConnectivityManager.EXTRA_NETWORK_INFO is deprecated. Now, you are suggested to use the CONNECTIVITY_SERVICE with getActiveNetworkInfo(), something like that:
#Override
public void onReceive(Context context, Intent intent) {
if (!ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
return;
}
ConnectivityManager connManager =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connManager == null) {
return;
}
NetworkInfo activeNetworkInfo = connManager.getActiveNetworkInfo();
boolean isConnected =
(activeNetworkInfo != null) && activeNetworkInfo.isConnected();
int networkType =
(activeNetworkInfo != null) ? activeNetworkInfo.getType() : -1;
// by using isConnected & networkType, get the new connectivity status..
}
This raises the following question:
In the 2nd implementation, when using the CONNECTIVITY_SERVICE and not the EXTRA_NETWORK_INFO:
Is there a chance for a race condition? As the onReceive is called asynchronously, when getting the network info from the connectivity service, may the connectivity state be different from the network info in the intent (as the connectivity might change in the meanwhile)?
Meaning, when using the intent, I am sure that the network info includes the data that triggered the onReceive, but when using the service - the network info might be different...?
If so, what's the best way to get the info that triggered the onReceive?
Also, if the only way to do so is to keep using the intent - doesn't that make 2 sources of truth? (one from the Connectivity Service, and one from the sent intent...)
UPDATE:
An example of the possible race condition:
Someone connected to WiFi and immediately disconnected from WiFi. This will result in 2 intents to be sent (one for connected to WiFi, and one for disconnecting from WiFi. More intents are actually sent, but I will focus on these 2). The possible race condition I am asking about is that: when we get the first intent (WiFi connected), the intent extra EXTRA_NETWORK_INFO will result in isConnected = true and networkType = TYPE_WIFI. But, is it possible that when I get the data from the Connectivity Service, as the onReceive is called asynchronously, the WiFi has already disconnected, resulting in isConnected = false and networkType = TYPE_WIFI while the intent still holds the right values for this call of onReceive? Or this will always happen fast enough so you get the right values from the Connectivity Service in the onReceive.. ?
My solution was sending a broadcast everytime connection changes adding an extra if the status of connection is up or down, hope it helps:
public class ConnectivityChangeReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i(context.getClass().getName(), " Changed connection ");
context.sendBroadcast(new Intent(MyApplication.ACTION_CONNECTION_CHANGED)
.putExtra(MyApplication.INTENT_EXTRA_CONNECTION_STATUS,NetWorkUtils.isNetworkAvailable(context)));
}}
public static boolean isNetworkAvailable(Context context) {
ConnectivityManager cm =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
return activeNetwork != null &&
activeNetwork.isConnectedOrConnecting();
}
Android manifest:
<receiver android:name=".ConnectivityChangeReceiver">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>

Android network state change detection takes time

I am trying to detect network state change in my android app. I followed the answer in that question : Check INTENT internet connection
This works, but it takes time for broadcastreceiver to detect changes. When i turn wifi on or off, about 10 seconds later the onReceive() method is called. Why is that taking so much time? Can anyone help?
Thanks
Here is my code:
public class NetworkStateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("app", "Network connectivity change");
if (intent.getExtras() != null) {
NetworkInfo ni = (NetworkInfo) intent.getExtras().get(
ConnectivityManager.EXTRA_NETWORK_INFO);
if (ni != null && ni.getState() == NetworkInfo.State.CONNECTED) {
Log.i("app", "Network " + ni.getTypeName() + " connected");
Toast.makeText(context, "CONNECTED", Toast.LENGTH_LONG).show();
} else if (intent.getBooleanExtra(
ConnectivityManager.EXTRA_NO_CONNECTIVITY, Boolean.FALSE)) {
Toast.makeText(context, "DISCONNECTED", Toast.LENGTH_LONG).show();
Log.d("app", "There's no network connectivity");
}
}
}
}
and in my Manifest's application tag:
<receiver android:name="com.mypackage.NetworkStateReceiver" >
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
I found the solution.
Instead of extending BroadcastReceiver class and creating NetworkStateChangeReceiver, i created a broadcastreceiver on my activity and registered it there. Now it works and onReceive() method is triggered immediately.
public static boolean isInternetAvailable(Context context) {
boolean isConnection = false;
ConnectivityManager connectivityManager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivityManager != null) {
NetworkInfo[] info = connectivityManager.getAllNetworkInfo();
if (info != null) {
for (int index = 0; index < info.length; index++) {
if (info[index].getState() == NetworkInfo.State.CONNECTED) {
isConnection = true;
break;
}
}
}
}
return isConnection;
}
I have similar code within my app to detect if there's a connection present, and prompt the user of connection state changes.
Within my application, it receives the connection change for disconnect within a second, however when you go to turn the WiFi on it takes around ~7 seconds before my app receives the change in connection state.
However I receive the state change exactly the same time Android makes the toast saying "Connected to Wi-Fi network [your network name]".
Chances are you'd be receiving the change simultaneously from when it connects to a network, and not from when you pushed the Wi-Fi toggle to turn it on.
Is there a particular reason you need that instant feedback from when the toggle is pressed?

How to detect changes in the network?

I need to know how I can detect a switch in Wi-Fi networks, albeit automatically or manually, it doesn't matter. Is there some kind of intent being broadcasted throughout the system when a switch is detected? Or do I have to manually check if a new network is selected by calling a method on a ConnectivityManager?
At this point in time, I have fixed this like this (haven't fully tested it yet as I don't have a second network available at the moment):
I extended the BroadcastReceiver class
private class NetworkSwitcher extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
return;
}
NetworkInfo networkInfo =
(NetworkInfo)intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()) {
if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
Log.d(TAG, "Network type: " + networkInfo.getTypeName() +
" Network subtype: " + networkInfo.getSubtypeName());
getOwnIpAddress();
mClient.updateUnicastSocket(mOwnAddress, mUnicastPort);
}
}
else {
Log.e(TAG, "Network connection lost");
}
}
}
I register this class as a receiver with a filter set to the ConnectivityManager.CONNECTIVITY_ACTION intent (setting it in onResume() and releasing it in onPause()). This ought to catch any automatic Wi-Fi network switch. The getOwnIpAddress retrieves the device's IP address from the WifiManager.
I've also found that it works when I return to the activity from another activity.

Broadcastreceiver to obtain ServiceState information

Does anyone know of a way to obtain the phone service state (IN_SERVICE, OUT_OF_SERVICE, EMERGENCY_ONLY, POWER_OFF) in android.
I was hoping there would be a broadcastreciever to identify the changes, but I can't find anything. I know there's a listener but I'm not sure how I would use that from my app as it runs as a service using a WakefulIntentService (by thecommonsguy).
With something like battery level (ie BATTERY_LOW, BATTERY_OKAY) it's quite easy, but I just can't work out a similar things for phone service changes.
Register a receiver for
public static final String ACTION_SERVICE_STATE_CHANGED = "android.intent.action.SERVICE_STATE";
When you get intent on your receiver just use below little hack from android source
public void onReceive(Context context, Intent intent) {
int state = intent.getExtras().getInt("state");
if(state == ServiceState.STATE_IN_SERVICE)
{
//Do whatever you want
}
}
check source of service state class
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/1.5_r4/android/telephony/ServiceState.java#ServiceState.setFromNotifierBundle%28android.os.Bundle%29
You could write your own BroadcastReceiver. Your receiver will receive connectivity changes and inform your desired instance about the change (for example your own CommunicationManager):
public class ConnectivityReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i(getClass().getName(), "A change in network connectivity has occurred. Notifying communication manager for further action.");
NetworkInfo info = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if(info != null) {
Log.v(getClass().getName(), "Reported connectivity status is " + info.getState() + ".");
}
CommunicationManager.updateConnectivityState(); // Notify connection manager
}
}
For example here your CommunicationManager instance, which will be notified about connectivity changes:
protected static void updateConnectivityState()
{
boolean isConnected = false;
if (_connec != null && (_connec.getNetworkInfo(1).getState() == NetworkInfo.State.CONNECTED) ||(_connec.getNetworkInfo(0).getState() == NetworkInfo.State.CONNECTED)){
isConnected = true;
Log.i(CommunicationManager.class.getName(), "Device is connected to the network. Online mode is available.");
}else if (_connec.getNetworkInfo(0).getState() == NetworkInfo.State.DISCONNECTED || _connec.getNetworkInfo(1).getState() == NetworkInfo.State.DISCONNECTED ) {
isConnected = false;
Log.w(CommunicationManager.class.getName(), "Device is NOT connected to the network. Offline mode.");
}
_isConnected = isConnected;
}
Check the NetworkInfo class for further details about connectivity availability.
Don't forget to register the ACCESS_NETWORK_STATE permisson in your manifest:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
I hope this helps. Regards

ANDROID: if WiFi is enabled AND active, launch an intent

This is what I would like to do :
=> IF WiFi is enabled AND active, launch an intent (in fact it's a WebView that gets its content=>the instructions of my app on the web)
=> IF NOT, then I would launch another intent so that I don't show a WebView with "Web page not available ... The Web page at http://www.mywebsite.com might be temporarily down or it may have moved ..."
I tought initially to use
if(wifi.isWifiEnabled())
but that does not say if the Wifi connection is ACTIVE or not. It says only that the user has turned the switch on. The device may or may not be connected... Is this correct ?
Then I tried to use :
if (wifi.getConnectionInfo().getSSID()!= null)
but I noticed that it returns a string even if the connection has been lost or has been disabled ... ?
How should I do then ?
wifi = (WifiManager)getSystemService(Context.WIFI_SERVICE);
Intent intent_instructions;
if (wifi.getConnectionInfo().getSSID()!= null){
Log.i("Hub", "WiFi is enabled AND active !");
Log.i("Hub", "SSID = "+wifi.getConnectionInfo().getSSID());
intent_instructions = new Intent(this, Instructions.class);
}else{
Log.i("Hub", "NO WiFi");
intent_instructions = new Intent(this, Instructions_No_WiFi.class);
}
this.startActivity(intent_instructions);
Is there a more general way to test if the device has the connectivity to the internet just before launching an intent ? be it through Wifi, 3G, etc ...
Thanks in advance for your help.
You can use the following code to check for connectivity:
private static boolean isConnected(Context context) {
ConnectivityManager connectivityManager = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = null;
if (connectivityManager != null) {
networkInfo =
connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
}
return networkInfo == null ? false : networkInfo.isConnected();
}
Please make sure that you've registered the android.net.conn.CONNECTIVITY_CHANGE intent in your Manifest, or else, you'll never receive a notification that you're online.
I've been struggling with this issue for the last couple of days and I just now realized that I needed to register CONNECTIVITY_CHANGE and not only WIFI_STATE_CHANGED.
Try android.net.ConnectivityManager.getActiveNetworkInfo(): if it returns null you have no connection; if it returns a NetworkInfo object, you can check the connection's state with NetworkInfo.getState(), and if it's NetworkInfo.State.CONNECTED then you're connected, else you're not.
You can do it as follows:
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)){
Log.d("WIFI", "WIFI has changed");
int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, -1);
Log.d("WIFI", "WIFI State = " + wifiState);
setCurrentWifiState(wifiState);
}
You will get 0,1,2,3 depending on which state the Wifi is in, so for example 2 is connecting, you can check the rest in the documents
In your BroadcastReceiver class:
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)){
boolean connected = intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false);
if (connected){
// start your service here
}
}
}
And in your AndroidManifest.xml make sure you register for the android.net.wifi.supplicant.CONNECTION_CHANGE broadcast intent.
<intent-filter >
<action android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
</intent-filter>
isConnected() doesnt work fully ok, research something else
final ConnectivityManager connMgr = (ConnectivityManager)
this.getSystemService(Context.CONNECTIVITY_SERVICE);
final android.net.NetworkInfo wifi =
connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
final android.net.NetworkInfo mobile =
connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
if( wifi.isAvailable() && wifi.getDetailedState() == DetailedState.CONNECTED){
Toast.makeText(this, "Wifi" , Toast.LENGTH_LONG).show();
}
else if( mobile.isAvailable() && mobile.getDetailedState() == DetailedState.CONNECTED ){
Toast.makeText(this, "Mobile 3G " , Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(this, "No Network " , Toast.LENGTH_LONG).show();
}
this code check if you are with wifi or 3g or nothing , in the case of wifi on but not connected to a net or 3g have signal problem it detect this details, with DetailedStates

Categories

Resources