CONNECTIVITY_ACTION intent received twice when Wifi connected
Asked Answered
V

14

52

In my app I have a BroadcastReceiver that is launched as a component through a <receiver> tag, filtering android.net.conn.CONNECTIVITY_CHANGE intents.

My goal is simply to know when a Wifi connection was established, so what I am doing in onReceive() is this:

NetworkInfo networkInfo = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if(networkInfo.getType() == ConnectivityManager.TYPE_WIFI && networkInfo.isConnected()) {
    // Wifi is connected
}

It works fine, but I always seem to get two identical intents within about one second when a Wifi connection is established. I tried to look at any info I could get from the intent, the ConnectivityManager and WifiManager, but I can't find anything that distinguishes the two intents.

Looking at the log, there is at least one other BroadcastReceiver that also receives the two identical intents.

It is running on a HTC Desire with Android 2.2

Any idea why I seem to get a "duplicated" intent when Wifi connects or what the difference between the two might be?

Velutinous answered 11/3, 2011 at 17:11 Comment(1)
youtube.com/playlist?list=PLrnPJCHvNZuBqr_0AS9BPXgU6gvNeai5S A good understanding of recent methods of implementing broadcast receiversHarping
V
63

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

Velutinous answered 4/5, 2011 at 21:31 Comment(7)
I'd like to mention here that, at least sometimes, the WifiManager.NETWORK_STATE_CHANGED_ACTION intent is broadcasted a short moment before the data connection is completely set up (DHCP configuration not complete) and that at that moment, ConnectivityManager.getActiveNetworkInfo() might still return a NetworkInfo with type TYPE_WIFI.Obelisk
I can personally confirm everything written by Torsten. Thank you for your solution. I find it frustrating that there is not a cleaner and simpler solution. The most surprising thing is that this behaviour doesn't take place when wifi is disconnected. I'm not saying it is necessarily a bug, but it could be better documented.Tapping
Getting the NetworkInfo from the intent is now depreciated. However its not clear why "they" don't expect this to remain stable. Is anyone finding instability in getting the Networkinfo from the intent?Mugger
@BrillPappin Getting it from ConnectivityManager is deprecated, but this answer's discussion is focused on getting it from WifiManager which is still fine. However, like the others I still get strange sequences of notifications.Methane
Thanks, I asked the accounts to be merged!Obelisk
intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO) is deprecated.Lais
android.net.conn.CONNECTIVITY_CHANGE is also deprecated in N, just letting future people be aware..Piscine
R
12

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

Rubdown answered 6/9, 2011 at 20:27 Comment(2)
both methods return true for both events for me :)Afflux
This answer is not correct, isConnected() can return true both times.Aurelie
C
7

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.

Cyclohexane answered 5/2, 2019 at 16:29 Comment(0)
R
4

Updated the code of Torsten, such that when WIFI gets disconnected, only the single appropriate broadcast is acted upon.

Used NetworkInfo.getDetailedState() == DetailedState.DISCONNECTED for the checking.

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.getDetailedState() == DetailedState.DISCONNECTED) {
            // Wifi is disconnected
            Log.d("Inetify","Wifi is disconnected: "+String.valueOf(networkInfo));
        }
    }
}
Remscheid answered 25/6, 2013 at 6:29 Comment(2)
For whoever wonders the change is if(networkInfo.getType() == ConnectivityManager.TYPE_WIFI && ! networkInfo.isConnected()) to if (networkInfo.getDetailedState() == DetailedState.DISCONNECTED)Easiness
What about Wifi and Cellular data? I believe both will cause the BroadcastReceiver to trigger causing more calls.Diploblastic
S
3

If you registered the activity as an intent listener then you will recieve the same message twice. Specifically, you need to choose whether you want to listen on the Package level (XML) or the programatic level.

If you set up a class for a broadcast reciever and attach the listen to it AND you attach an intent filter to the activity, then the message will be replicated twice.

I hope this solves your issue.

Statolatry answered 4/5, 2011 at 21:49 Comment(2)
This is correct, you either create a Broadcast listener in the class OR the manifest, but not both.Borman
Finally I can comment on this one... your answer is certainly correct, but I had registered the BroadcastReceiver only in the manifest.Obelisk
R
2

I solved twice call by using SharedPref with Time.

private static final Long SYNCTIME = 800L;
private static final String LASTTIMESYNC = "DATE";
SharedPreferences sharedPreferences;
private static final String TAG = "Connection";

@Override
public void onReceive(Context context, Intent intent) {
     Log.d(TAG, "Network connectivity change");
     sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);

     final ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
     final NetworkInfo ni = connectivityManager.getActiveNetworkInfo();

        if (ni != null && ni.isConnected()) {   

            if(System.currentTimeMillis()-sharedPreferences.getLong(LASTTIMESYNC, 0)>=SYNCTIME)
            {
                sharedPreferences.edit().putLong(LASTTIMESYNC, System.currentTimeMillis()).commit();
                // Your code Here.
            }
     }
     else if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, Boolean.FALSE)) {
            Log.d(TAG, "There's no network connectivity");

    }
}

Because there is small delay between 1.call and 2.call (About 200 milisec). So in IF with time second call will stop and just first will continue.

Rambow answered 14/8, 2014 at 21:31 Comment(1)
As bad as this one sounds as a solution, it worked for me. I have tried semaphores and also synchronized blocks and none have worked well for me.Diploblastic
F
2

I solved if with in

onCreate()
       intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
       intentFilter.addAction("android.net.wifi.WIFI_STATE_CHANGED");
       intentFilter.addAction("android.net.wifi.STATE_CHANGE");
       ctx.registerReceiver(outgoingReceiver, intentFilter);

in

BroadcastReceiver
 public void onReceive(Context context, Intent intent) {
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 connected
                    Log.d("Inetify", "Wifi is connected: " + String.valueOf(networkInfo));

                    Log.e("intent action", intent.getAction());
                    if (isNetworkConnected(context)){
                        Log.e("WiFi", "is Connected. Saving...");
                        try {
                            saveFilesToServer("/" + ctx.getString(R.string.app_name).replaceAll(" ", "_") + "/Temp.txt");
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }}


 boolean isNetworkConnected(Context context) {
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo ni = cm.getActiveNetworkInfo();
        if (ni != null) {
            Log.e("NetworkInfo", "!=null");

            try{
                //For 3G check
                boolean is3g = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE)
                        .isConnectedOrConnecting();
                //For WiFi Check
                boolean isWifi = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI)
                        .isConnected();

                Log.e("isWifi", "isWifi="+isWifi);
                Log.e("is3g", "is3g="+is3g);
                if (!isWifi)
                {

                    return false;
                }
                else
                {
                    return true;
                }

            }catch (Exception er){
                return false;
            }

        } else{
            Log.e("NetworkInfo", "==null");
            return false;
        }
    }
Fortyfour answered 26/8, 2015 at 21:16 Comment(0)
R
1

I solved this problem by using the intent extra for NetworkInfo. In the example below, onReceive event is fired only once if wifi is connected or mobile.

if (intent.getAction().equalsIgnoreCase(ConnectivityManager.CONNECTIVITY_ACTION)) {

        NetworkInfo networkInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);

        boolean screenIsOn = false;
        // Prüfen ob Screen on ist
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            screenIsOn = pm.isInteractive();
        } else {
            screenIsOn = pm.isScreenOn();
        }

        if (Helper.isNetworkConnected(context)) {
            if (networkInfo.isConnected() && networkInfo.isAvailable()) {
                Log.v(logTAG + "onReceive", "connected");

                if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
                    Log.v(logTAG + "onReceive", "mobile connected");

                } else if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
                    Log.v(logTAG + "onReceive", "wifi connected");
                }
            }
        }

and my helper:

    public static boolean isNetworkConnected(Context ctx) {
    ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo ni = cm.getActiveNetworkInfo();

    return ni != null;
}
Ravenravening answered 11/2, 2017 at 14:0 Comment(0)
S
1

If you only want to receive it once, you can simply control it through variables.

 if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
                NetworkInfo activeNetwork = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
                if (activeNetwork != null) { // connected to the internet
                    if (activeNetwork.isConnected() && !isUpdated) {
                        if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
                            // connected to wifi
                        } else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
                            // connected to the mobile provider's data plan
                        }

                        isUpdated = true;
                    } else {
                        isUpdated = false;
                    }
                }
            }
Synchronize answered 28/4, 2018 at 6:28 Comment(0)
C
1

When turning WIFI ON,

  1. With MOBILE data ON, two broadcasts are sent: Broadcast #1 : MOBILE data disconnected, and Broadcast #2 : WIFI connected
  2. With MOBILE data OFF, only one broadcast is sent: Broadcast #1 : WIFI connected

Similar behavior can be observed while turning the WIFI OFF under the above two conditions.

To distinguish between the two, please follow #2 and #3 below:

        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d(TAG, "*** Action: " + intent.getParcelableExtra("networkInfo"));

            NetworkInfo netInfo = intent.getParcelableExtra("networkInfo");

            if(intent.getAction().equalsIgnoreCase("android.net.conn.CONNECTIVITY_CHANGE")) {
                ConnectivityManager connectivityManager
                        = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
                NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
                if (activeNetInfo != null) {
                    if (netInfo.getType() == ConnectivityManager.TYPE_WIFI) {
                        if (netInfo.getState().name().contains("DISCONNECTED")
                                && activeNetInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
                            Log.d(TAG, "WIFI disconnect created this broadcast. MOBILE data ON."); // #1
                        } else if (netInfo.getState().name().contains("CONNECTED")
                                && activeNetInfo.getType() == ConnectivityManager.TYPE_WIFI) {
                            Log.d(TAG, "WIFI connect created this broadcast."); // #2
                        }
                    } else if (netInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
                        if (netInfo.getState().name().contains("DISCONNECTED")
                                && activeNetInfo.getType() == ConnectivityManager.TYPE_WIFI) {
                            Log.d(TAG, "MOBILE data disconnect created this broadcast. WIFI ON."); // #3
                        } else if (netInfo.getState().name().contains("CONNECTED")
                                && activeNetInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
                            Log.d(TAG, "MOBILE data connect created this broadcast."); // #4
                        }
                    }
                } else {
                    Log.d(TAG, "No network available");
                }
            }
        }
Carousel answered 14/9, 2018 at 16:29 Comment(0)
C
0

The way I handled it, was simply by saving the state of the network and then comparing it to see if there was a change.

public class ConnectivityChangedReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        boolean previouslyConnected = MyApp.getInstance().isNetworkPreviouslyConnected();
        boolean currentlyConnected = MyApp.getInstance().isNetworkConnected();

        if (previouslyConnected != currentlyConnected) {
            // do something and reset
            MyApp.getInstance().resetNetworkPreviouslyConnected();
        }
    }

}

If this is the approach you take, it's important to reset it in onResume of your fragment or activity, so that it holds the current value:

@Override
public void onResume() {
    super.onResume();
    MyApp.getInstance().resetNetworkPreviouslyConnected();
}

I did that in my BaseFragment, a parent of all fragments in my app.

Cheque answered 23/3, 2015 at 18:57 Comment(0)
F
0

check networkType from intent and compare activeNetworkInfo.getType()

                 Bundle bundle = intent.getExtras();
                 ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
                 NetworkInfo ni = manager.getActiveNetworkInfo();

                 if(ni != null && ni.getState() == NetworkInfo.State.CONNECTED) {
                     if(bundle.getInt("networkType") == ni.getType()) {
                         // active network intent
                     }
                 }
Formally answered 29/9, 2016 at 1:9 Comment(0)
S
0

Found a special case for network connectivity saying there is no internet but actually there is. It turns out getActiveNetworkInfo will always return you DISCONNECTED/BLOCKED in a specific case when network is changed while battery level is low and app was just switched

Check out this post

Stepfather answered 21/7, 2017 at 7:48 Comment(0)
I
-2

Only listen to action "android.net.conn.CONNECTIVITY_CHANGE". It's broadcasted whenever connection is established or destroyed.

"android.net.wifi.STATE_CHANGE" will be broadcasted when connection established. So you get two triggers.

Enjoy!

Instability answered 19/10, 2011 at 2:33 Comment(1)
The problem is that it sends two events for each sate change.Mugger

© 2022 - 2024 — McMap. All rights reserved.