send broadcast with combination of localbroadcastmanager sendorderedbroadcast
Asked Answered
A

4

7

I'm wanting to implement what CommonsWare describes on this blog post: http://commonsware.com/blog/2010/08/11/activity-notification-ordered-broadcast.html. The post makes sense, and I was able to browse the example source here: https://github.com/commonsguy/cw-advandroid/tree/master/Broadcast.

What I'm curious about is if calling LocalBroadcastManager.getInstance(UnzipService.this).sendBroadcast(broadcast); inside of a service will still be picked up by a broadcast receiver of the type you define in your manifest.

In case what I'm asking isn't clear, what I'm trying to do is use the LocalBroadcastManager because the broadcasts from my service don't necessarily need to be seen system wide and I'd rather keep them private if possible, but I also want to display notifications if the user closes my app and the service is still running. Is there a way to combine both of those capabilities without sending a broadcast twice inside of the service?

(What I don't want to have to do) like: LocalBroadcastManager.getInstance(UnzipService.this).sendBroadcast(broadcast); sendOrderedBroadcast(broadcast);

Argybargy answered 18/12, 2012 at 20:30 Comment(0)
A
11

What I'm curious about is if calling LocalBroadcastManager.getInstance(UnzipService.this).sendBroadcast(broadcast); inside of a service will still be picked up by a broadcast receiver of the type you define in your manifest.

No. LocalBroadcastManager only works with receivers registered with the LocalBroadcastManager singleton itself. Moreover, LocalBroadcastManager does not support ordered broadcasts, last I checked.

what I'm trying to do is use the LocalBroadcastManager because the broadcasts from my service don't necessarily need to be seen system wide and I'd rather keep them private if possible

So long as you are not using an <intent-filter> on your BroadcastReceiver in the manifest, and therefore are using an explicit Intent as the broadcast itself, your broadcast will only be seen by yourself and the bit of the OS that manages broadcasts. Other apps will not be able to spy upon it.

Agretha answered 18/12, 2012 at 20:38 Comment(8)
mmm... so if i want broadcasts to appear in the notification bar area, localbroadcastmanager wont suffice. However, the broadcasts sent by 'sendOrderedBroadcast' should be private for the most part 'so long as...' i don't use an <intent-filter> on my broadcast receiver in the manifest, but i thought it needed that? <receiver android:name=".DataLoadReceiver"> <intent-filter> <action android:name="com.something.something.DATALOADRECEIVER" /> </intent-filter> </receiver>Argybargy
@Josh: "but i thought it needed that?" -- ah, well, in general, you don't. For the specific pattern that I outlined, where there are multiple possible receivers in your app, you do -- my apologies for forgetting that was your root scenario. In that case, you can use setPackage() on the Intent to restrict its scope to only your app's components.Agretha
sorry, just want to make double sure I understand. In the example on your site, you were assuming the case where the developer had multiple possible receivers in their app. However since I'm shooting for the broadcasts only being picked up by my one receiver in my app, I can use setPackage() on the Intent I broadcast, keeping it somewhat privateArgybargy
@Josh: "However since I'm shooting for the broadcasts only being picked up by my one receiver in my app" -- not if you are trying to achieve what I outlined in my blog post. In that blog post, you have two BroadcastReceivers: one registered by the Activity, and one registered in the manifest. However, they are both in your app, and so setPackage() is helpful for privacy.Agretha
ah yes, forgot there were 2. man this stuff isn't straightforward at first. thanks for the help! ill give it a shotArgybargy
so if I'm not specifying an <intent-filter> in the manifest, and using setPackage() on the broadcast, I'm sending my broadcast like private static Intent broadcast = new Intent(); broadcast.setPackage(getPackageName()); sendOrderedBroadcast(broadcast, null);. and i changed the IntentFilter object in my activity to have a filter for getPackageName(). Do those seem like the appropriate changes? Maybe I've messed something up some place else? It seems my broadcast was never received.Argybargy
it also seems like, based on this stack overflow thread, that this setPackage thing isn't going to work for me on android devices < 4.0Argybargy
@Josh: You can use the permission-based approach, using a signature-level permission, so that only your app(s) can hold the permission and therefore only they will be able to access the broadcast.Agretha
B
3

If you only have 2 objects that might handle your broadcast (in your case an Activity and a notifications controller), you can achieve the behavior of a ordered broadcast using only the LocalBroadcastManager.

The general idea is:

  1. Set up your Service so that it broadcasts an Intent to your Activity with a particular action when you want to display your result
  2. In your Activity create a BroadcastReceiver that handles your Service result Intent, and register it on the LocalBroadcastManager with an IntentFilter using the action from step 1
  3. In your Service, when the results are available, try to send the result Intent using LocalBroadcastManager.getInstance(Context).sendBroadcast(Intent) this method returns a boolean that indicates if the broadcast has been sent to at least one receiver. If this boolean is false, it means that your Activity didn't handle your broadcast and you should show a notification instead.

In your service:

public UnzipService extends IntentService {

  public static final String ACTION_SHOWRESULT = UnzipService.class.getCanonicalName() + ".ACTION_SHOWRESULT";

  @Override
  protected void onHandleIntent(Intent intent) {
    Thread.sleep(500); // Do the hard work

    // Then try to notify the Activity about the results
    Intent activityIntent = new Intent(this, YourActivity.class);
    activityIntent.setAction(ACTION_SHOWRESULT);
    activityIntent.putExtra(SOME_KEY, SOME_RESULTVALUE); // Put the result into extras

    boolean broadcastEnqueued = LocalBroadcastManager.getInstance(this).sendBroadcast(activityIntent);

    if (!broadcastEnqueued) { // Fallback to notification!
      PendingIntent pendingIntent = PendingIntent.getActivity(this, (int) System.currentTimeMillis(), activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
      ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
        .notify(SOME_ID, new NotificationCompat.Builder(this)
          .setContentIntent(pendingIntent)
          .setTicker("results available")
          .setContentText("results")
          .build());
    }
  }
}

In your Activity:

public YourActivity extends Activity {
  private BroadcastReceiver resultReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      processResult(intent); // Results Intent received through local broadcast
    }
  }
  private IntentFilter resultFilter = new IntentFilter(UnzipService.ACTION_SHOWRESULT);

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate();
    Intent intent = getIntent();
    if (UnzipService.ACTION_SHOWRESULT.equals(intent.getAction())) {
      // The Activity has been launched with a tap on the notification
      processResult(intent); // Results Intent contained in the notification PendingIntent
    }
  }

  @Override
  protected void onResume() {
    super.onResume();
    LocalBroadcastManager.getInstance(this)
      .registerReceiver(resultReceiver, resultFilter);
  }

  @Override
  protected void onPause() {
    LocalBroadcastManager.getInstance(this)
      .unregisterReceiver(resultReceiver);
    super.onPause();
  }

  private void processResult(Intent intent) {
    // Show the results from Intent extras
  }
}

This should be a complete working example.

I hope this helps who is trying to implement ordered broadcasts with LocalBroadcastManager from support library!

Beefburger answered 4/3, 2015 at 21:52 Comment(0)
I
1

I understand you want to achieve the following:

"I have an event that occurs in the background. I want to update my activity, if the activity is on the screen. Otherwise, I want to raise a Notification." (@TheCommonsBlog)

You can achieve this behaviour by implementing a ResultReceiver. Examples Restful API service and http://itekblog.com/background-processing-with-intentservice-class/

What you basically do is instance a ResultReceiver in your Activity and pass it to the Service like a Parcelable parameter through an intent. Then, each time your service whats to update the UI, the service verifies the ResultReceiver object for NULL. If not NULL, you update the Ui via the onReceiveResult interface. Else, you raise a notification. When your activity dismisses, make sure you set the ResultReceiver on the Service to NULL.

Hope it helps.

PS: IMO, broadcasts are too much work and hard to control.

Indicia answered 18/12, 2012 at 20:44 Comment(2)
This looks like it would work, but it isn't right for me because the way the app is currently written (I didn't write it, I'm adding to it), the service is started before this activity is shown. A good alternate way of doing things thoughArgybargy
The order of execution does not matter. You can call StartService() from the Activity adding the ResultReceiver and your Service should only execute onStartCommand and not onStart. You could catch the ResultReceiver on the onStartCommand and handle the Ui events.Indicia
R
-2

Use LocalBroadcastManager and broadcasts become easy to use.

I am not in favor of updating an Activity if an event occurs in the background. The user might already be doing something else in the Activity. Seems to me that a Notification is sufficient; it's always visible and remains until the user dismisses it. Gmail and Gcal work like this; Gmail doesn't update the current screen if a new mail comes in. If you want to know how to handle the task flow for handling a notification when the user is already in the app, see the Notifications API guide and also the [Notifying The User2 training class.

Reseta answered 18/12, 2012 at 21:10 Comment(1)
"I am not in favor of updating an Activity if an event occurs in the background. The user might already be doing something else in the Activity." -- IMHO, that is not a sensible blanket statement to make. By that argument, Google Nav should never show the user their current location or the next turn, as finding out about location changes occurs in the background. "Gmail doesn't update the current screen if a new mail comes in" -- it does on my Galaxy Nexus running Android 4.2.1 (Priority Inbox updates on its own to reflect newly-received email, with no Notification).Agretha

© 2022 - 2024 — McMap. All rights reserved.