34

I registered a receiver that listens to network events:

<receiver 
    android:label="NetworkConnection"
    android:name=".ConnectionChangeReceiver" >
    <intent-filter >
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
</receiver>

receiver is also very simple:

public class ConnectionChangeReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
        if (activeNetInfo != null) {
                Log.v("@@@","Receiver : " + activeNetInfo);
        } else {
            Log.v("@@@","Receiver : " + "No network");
        }
    }
}

The problem is, when Wifi is connected I receive 3 identical messages in a row, like this:

Receiver : NetworkInfo: type: WIFI[], state: CONNECTED/CONNECTED, reason: (unspecified), extra: (none), roaming: false, failover: false, isAvailable: true
Receiver : NetworkInfo: type: WIFI[], state: CONNECTED/CONNECTED, reason: (unspecified), extra: (none), roaming: false, failover: false, isAvailable: true
Receiver : NetworkInfo: type: WIFI[], state: CONNECTED/CONNECTED, reason: (unspecified), extra: (none), roaming: false, failover: false, isAvailable: true

They are all "CONNECTED/CONNECTED" (Shouldn't they be something like CONNECTING/OBTAINING_IPADDR, etc.), so the problem is how do I tell when It's really connected? I have some routines that I want to make when wifi is actually connected, and I dont want them to be called three times in a row.

PS: 3G sends only one message, so no problem here.

Update:

Seems like it's device specific problem.

For test I took 2 Desire HD, and 4 random android phones(different Aquos models and some no-name chinese stuff). On both DHD and one random phone on wifi connect I got 3 messages, on remaining phones I got only one message. WTF.

Sver
  • 3,349
  • 6
  • 32
  • 53
  • How much time is between the logs? Can you see the currentTimeMillis() of when the events happen? I'm wondering if they are happening so closely together that the getActiveNetworkInfo() is actually returning the same object (a static object) which is being logged 3 times. – brianestey Dec 07 '11 at 09:16
  • 17:51:50.023 17:51:50.414 17:51:50.617 – Sver Dec 07 '11 at 09:21
  • I took your code and tried it on my phone (Samsung Galaxy S2). It was giving messages related to mobile network as well as wifi. When I turned off packet data, I only get one message each time I turn wifi on or off. It's weird that your messages are all the same though as mine were all different. – brianestey Dec 07 '11 at 09:36
  • Hm, is see.. I'll try making clean project with only this receiver, and see what happens. – Sver Dec 07 '11 at 09:39
  • Wow, yeah, I it a device problem. I updated my question. – Sver Dec 07 '11 at 09:55
  • I'm just guessing, but does those phone have anything in common? maybe the ROM has to do something with that. – SERPRO Dec 07 '11 at 10:01
  • I am not sure about the answer that it is a **device specific issue**, rather I will say that if you are calling `registerReceiver()` then you also need to call `unregisterReceiver()`, I can say this answer in case of `LocalBroadcastReceiver` – activesince93 Nov 19 '16 at 11:39

6 Answers6

58

Receiving multiple broadcast is a device specific problem. Some phones just send one broadcast while other send 2 or 3. But there is a work around:

Assuming you get the disconnect message when the wifi is disconnected, I would guess the first one is the correct one and the other 2 are just echoes for some reason.

To know that the message has been called, you could have a static boolean that gets toggled between connect and disconnect and only call your sub-routines when you receive a connection and the boolean is true. Something like:

public class ConnectionChangeReceiver extends BroadcastReceiver {
    private static boolean firstConnect = true;

    @Override
    public void onReceive(Context context, Intent intent) {
        final ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        final NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
        if (activeNetInfo != null) {
            if(firstConnect) { 
                // do subroutines here
                firstConnect = false;
            }
        }
        else {
            firstConnect= true;
        }
    }
}
Sheraz Ahmad Khilji
  • 8,300
  • 9
  • 52
  • 84
brianestey
  • 8,202
  • 5
  • 33
  • 48
  • 4
    Thank you! This is really nice and simple idea. There are two things you need to watch for. First - store "firstConnect" somewhere, for example in shared preferences, and second is when you change from 3G to WiFi, there is no actual disconnect, so It's better to handle 3G and WiFi events separately. – Sver Dec 07 '11 at 10:41
  • How can you call Activity mehtods from this class? It is not initiated by the activity so I cannot send context to this class. – KasparTr Apr 29 '16 at 13:08
  • Worked on an app using this trick. It's a mess. But is the "device-specific problem" just for network broadcasts, or do arbitrary broadcasts get duplicated? – Blaisorblade Sep 04 '16 at 10:55
  • Thanks @Sver. That was very helpful. – Rushi M Thakker Dec 29 '16 at 09:06
9

You can also cache in a static field the last handled connection type and check against the incomming broadcasts. This way you will only get one broadcast per connection type.

When connection type gets changed it will obviously work. When device gets out of connection activeNetworkInfo will be null and currentType will be NO_CONNECTION_TYPE as in default case.

public class ConnectivityReceiver extends BroadcastReceiver {

    /** The absence of a connection type. */
    private static final int NO_CONNECTION_TYPE = -1;

    /** The last processed network type. */
    private static int sLastType = NO_CONNECTION_TYPE;

    @Override
    public void onReceive(Context context, Intent intent) {
        ConnectivityManager connectivityManager = (ConnectivityManager)
                context.getSystemService(Context.CONNECTIVITY_SERVICE);

        NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
        final int currentType = activeNetworkInfo != null
                ? activeNetworkInfo.getType() : NO_CONNECTION_TYPE;

        // Avoid handling multiple broadcasts for the same connection type
        if (sLastType != currentType) {
            if (activeNetworkInfo != null) {
                boolean isConnectedOrConnecting = activeNetworkInfo.isConnectedOrConnecting();
                boolean isWiFi = ConnectivityManager.TYPE_WIFI == currentType;
                boolean isMobile = ConnectivityManager.TYPE_MOBILE == currentType;

                // TODO Connected. Do your stuff!
            } else {
                // TODO Disconnected. Do your stuff!
            }

            sLastType = currentType;
        }
}
Vishal Yadav
  • 3,642
  • 3
  • 25
  • 42
Aleksandar Ilic
  • 1,521
  • 16
  • 19
3

In my case, I was registering my BroadcastReceivers in onResume and only unregistering them in onDestroy.

This caused each broadcastreceiver to be registered 3 or 4 times depending on how many times the activity resumes.

Setting your broadcastreceiver in the right place in terms of the activity lifecycle will allow you to stop getting multiple confusing calls.

Simon
  • 19,658
  • 27
  • 149
  • 217
1

Register your LocalBroadcastreceiver in oncreate() itself not in onResume(). unregistered in onDestroy

theoretisch
  • 1,718
  • 5
  • 24
  • 34
0

I have an application that uploads data when the user comes back online. Since my broadcast receiver can receive the intent multiple times, it can lead to the data being uploaded more than once. To handle this, I use a service that will not do anything if it is already running.

Broadcast Receiver:

public class ConnectionChangeReceiver extends BroadcastReceiver {
    private static boolean firstConnect = true;

    @Override
    public void onReceive(Context context, Intent intent) {
        final ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        final NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
        if (activeNetInfo != null) {
            startService();
        }   
    }
}

Service:

public class MyService extends Service {
    private boolean mRunning;

    @Override
    public void onCreate() {
        super.onCreate();
        mRunning = false;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (!mRunning) {
            mRunning = true;
            uploadTheData();
        }
        return super.onStartCommand(intent, flags, startId);
    }
}
John
  • 460
  • 1
  • 5
  • 18
0

My concern with the approach proposed by Aleksander is that it doesn't consider network changes of the same type, e.g. from one WiFi network to another.

I'm proposing to compare the extraInfo of the active network, which contains the network name, e.g. WiFi SSID or mobile network name like VZW

 String currentNetworkName = "";

 ConnectivityManager connectivityManager =
            ((ConnectivityManager) context.getSystemService(
                    Context.CONNECTIVITY_SERVICE));

    NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
    boolean connected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
    if (connected) {
        // prevent duplicate connect broadcasts
        String extraInfo = activeNetwork.getExtraInfo();
        if(! currentNetworkName.equals(extraInfo)) {
            // to do: handle network changes
            currentNetworkName = extraInfo;
        }
    } else {
        Log.d(TAG, "is not connected");
        isConnected = false;
        currentNetworkName = "";
    }
checkmate711
  • 3,301
  • 2
  • 35
  • 45