简体   繁体   中英

android broadcast receiver OnReceive() delay causing errors

My Broadcast Receiver listens for WiFi ssid change and if the ssid changes it return the boolean WifiChanged true.I check for this boolean in another activity which changes a list based on the value returned whether it was true or false.By default the boolean value is false.

I intentionally change the wifi which should trigger the broadcast receiver to return the boolean value to true and set the list accordingly but what actually happens is that my list is changed based on the boolean value false as broadcast receiver takes a while to return the value. In the log below you can see the boolean value is false and after 0.58 seconds approximately the ssid changes. By that time its too late

08-08 16:43:54.487: D/PlayerManager(20733): Did Wi-Fi Changed: false
   |
   |
   |
08-08 16:43:55.047: V/ConnectionChangeReceiver(20733): onReceive(Context context, Intent intent)
08-08 16:43:55.077: D/ConnectionChangeReceiver(20733): ssid changed from s_ssid="Walter_Meth_Lab" to newSsid="Kings_Landing"

Here is my OnReceive()

public class ConnectionChangeReceiver extends BroadcastReceiver {
private static final String TAG = "ConnectionChangeReceiver";
private static String s_ssid = null;
private static String s_ipAddress = null;
private static String mNetworkType;
private static ConnectionChangeReceiver sInstance;

private ConnectionChangeListener mConnectionChangeListener;
private boolean mHasWifiChanged;

public static ConnectionChangeReceiver getInstance() {
    Log.v(TAG, "getInstance()");
    synchronized (ConnectionChangeReceiver.class) {
        if (sInstance == null) {
            sInstance = new ConnectionChangeReceiver();
        }
    }
    return sInstance;
}

public boolean WifiChanged() {
    return mHasWifiChanged;
}


public void setConnectionChangeListener(final ConnectionChangeListener listener) {
    this.mConnectionChangeListener = listener;
}

@Override
public void onReceive(final Context context, final Intent intent) {
Log.v(TAG, "onReceive(Context context, Intent intent)");

mHasWifiChanged = false;
String newSsid = null;

String action = intent.getAction();
if ((action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) || (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION))
        || (action.equals("android.net.conn.CONNECTIVITY_CHANGE"))) {
    NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
    if (networkInfo != null) {
        if (networkInfo.getTypeName().equals("WIFI")) {
            WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
            if (wm != null) {
                WifiInfo connectionInfo = wm.getConnectionInfo();
                if (connectionInfo != null) {
                    newSsid = connectionInfo.getSSID();
                    if ((newSsid != null) && (s_ssid != null) && (newSsid.compareTo(s_ssid) != 0)) {
                        Log.d(TAG, "ssid changed from s_ssid=" + s_ssid + " to newSsid=" + newSsid);
                        mHasWifiChanged = true;
                    }
                }
            }
        }
    }
}

s_ssid = newSsid;

This is the other activity where i use the boolean

boolean WifiChanged = ConnectionChangeReceiver.getInstance().WifiChanged();
        Log.d(TAG, "Did Wi-Fi Changed:" + WifiChanged);
        if (WifiChanged) {
            //Do Something

        }

If the Wi-Fi ssid is changed, the list should change based the WifiChanged true but it always changes for WifiChanged false as ChangeReceiver() does not return true on time and the default false is used.

There are many ways to get this done.

I prefer using the LocalBroadcastManager .

How this will work: instead of having the activity ask for information, let ConnectionChangeReceiver deliver it - as and when it is available . Once ConnectionChangeReceiver is done with processing the received intent , it sends out a local broadcast. If your activity is alive and listening, it reacts to this.

// Snippet from your `ConnectionChangeReceiver # onReceive(...)` method
if (connectionInfo != null) {
    newSsid = connectionInfo.getSSID();
    if ((newSsid != null) && (s_ssid != null) && (newSsid.compareTo(s_ssid) != 0)) {
        Log.d(TAG, "ssid changed from s_ssid=" + s_ssid + " to newSsid=" + newSsid);
        mHasWifiChanged = true;

        // We can send a local broadcast now
        // Note that the String `"com.my.app.wifi.WIFI_RELATED_CHANGE"` can be 
        // customized to your preference
        Intent localIntent = new Intent("com.my.app.wifi.WIFI_RELATED_CHANGE");

        // Including this extra is redundant here since `mHasWifiChanged` will
        // always be true at this point. I am including it for example sake.
        // Again, notice that the key `"com.my.app.wifi.WIFI_HAS_CHANGED"` can
        // be customized
        localIntent.putExtra("com.my.app.wifi.WIFI_HAS_CHANGED", mHasWifiChanged);

        // Broadcasts the Intent to receivers in this app
        LocalBroadcastManager.getInstance(context).sendBroadcast(localIntent);
    }
}

On the activity side: create a BroadcastReceiver in your activity and set it to listen for action "com.my.app.wifi.WIFI_RELATED_CHANGE" . This String value must be the same as the one used when sending the broadcast from your ConnectionChangeReceiver # onReceive(...) :

// Declared as a class member in your Activity
BroadcastReceiver wifiRelatedChangeListener = new BroadcastReceiver() {

    @Override
    public void onReceive(Context context, Intent intent) {

        String action = intent.getAction();
        if (action.equals("com.my.app.wifi.WIFI_RELATED_CHANGE")) {
            // In your current setup, `mHasWifiChanged` will always be true
            // Act on it
        }
    }
};

You should declare the BroadcastReceiver as a class member. This will allow you to register and unregister the BroadcastReceiver appropriately: register in onResume() and unregister in onPause() .

In onResume() of your activity, register this receiver to listen for action com.my.app.wifi.WIFI_RELATED_CHANGE :

@Override
public void onResume() {
    super.onResume();

    // Activity has come to foreground. Register to listen for changes to wifi state.

    // Create an IntentFilter with action `com.my.app.wifi.WIFI_RELATED_CHANGE`
    IntentFilter intentFilter = new IntentFilter("com.my.app.wifi.WIFI_RELATED_CHANGE");

    // Register your broadcastreceiver to receive broadcasts 
    // with action `com.my.app.wifi.WIFI_RELATED_CHANGE`
    LocalBroadcastManager.getInstance(this)
                         .registerReceiver(wifiRelatedChangeListener, intentFilter);
}

Unregister the reciever in onPause() :

@Override
public void onPause() {
    super.onPause();

    // Activity is going to background. No need to listen anymore
    LocalBroadcastManager.getInstance(this).unregisterReceiver(wifiRelatedChangeListener);
}

Note that you can declare a receiver such as wifiRelatedChangeListener in however many activities. The broadcast is sent once from ConnectionChangeReceiver # onReceive(...) . Whichever activity is in the foreground/listening at that point, will receive this broadcast and act upon it.

By that time its too late

This issue will not be present in the solution discussed above.

You have an incorrect approach. Your broadcast receiver should send a message to your Activity, and then your Activity should check the WiFi status using the method "onNewIntent". Then if you are setting a static variable in your receiver (which you probably shouldn't do) you could check it, but better just send your Activity a Boolean "extra" with the WiFi status.

The problem is that your UI thread is much faster than a background thread. So if an Activity is responding to an event that a receiver is also responding to, it's very likely Android will prioritize the Activity's processing over the background thread.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM