Tony the Pony
Tony the Pony

Reputation: 41347

Updating an Activity from a BroadcastReceiver

This question brought up an interesting issue.

The OP has an app that displays a map, and this map needs to be updated with location markers that are received via SMS messages. The individual steps are fairly straightforward: the SMS messages can be received by a BroadcastReceiver, the markers can be displayed by an ItemizedOverlay on top of a MapView. The tricky part is to have the receiving part communicate with the main part of the app.

Upvotes: 3

Views: 4220

Answers (3)

Maragues
Maragues

Reputation: 38324

Reading the docs, it looks like a BroadcastReceiver is executed on a different process, tho I'm not 100% sure (BroadcastReceiver lifecycle)

A process that is currently executing a BroadcastReceiver (that is, currently running the code in its onReceive(Context, Intent) method) is considered to be a foreground process

This said, I wouldn't consider safe to access the activity from the onReceive, as it's a different process and it will probably crash.

Take into account that the Activity can also act as a BroadcastReceiver, tho you have to control when in its lifecycle it actively listens for events. That way, you can subscribe in onResume (code extracted from ZXing project)

 public void onResume(){
    activity.registerReceiver(powerStatusReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
    [...]
  }

  public void onPause() {
    [...]
    activity.unregisterReceiver(powerStatusReceiver);
  }

And you define the BroadcastReceiver as a private class inside the public class

final class InactivityTimer {

[onResume, onPause, rest of the stuff ...]

    private final class PowerStatusReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent){
          if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
            // 0 indicates that we're on battery
            // In Android 2.0+, use BatteryManager.EXTRA_PLUGGED
            int batteryPlugged = intent.getIntExtra("plugged", -1);
            if (batteryPlugged > 0) {
              InactivityTimer.this.cancel();
            }
          }
        }
      }
}

So, the BroadcastReceiver should always persist the new markers (through a Service, never inside the onReceive) AND it should notify a potentially active MapActivity that new markers have been added, which will be listening if it's active.

Or, even easier, the Activity and the BroadcastReceiver listen for the same SMS Intent. While the latter persists it, the first updates the map, tho I'm just guessing what I would try.

Upvotes: 4

Tony the Pony
Tony the Pony

Reputation: 41347

The best approach, as others have pointed out, seems to be to create a separate intent that is private to the app. Instead of declaring it in the manifest, the activity registers it whenever it is active. This answer explains how to this.

The public BroadcastReceiver (which is declared in the manifest and handles the android.provider.Telephony.SMS_RECEIVED) should then invoke this application-specific intent.

Upvotes: 0

kabuko
kabuko

Reputation: 36302

The BroadcastReceiver should run in the same process. BroadcastReceiver is designed to be short-lived. As such it can execute without any real worry about suspending the foreground Activity. You could potentially access the Activity directly via static reference assuming you checked for the case where the Activity has not yet been created. However, it might make more sense to communicate via Intents.

Upvotes: 1

Related Questions