52

So I understand (I think) about broadcast intents and receiving messages to them.

So now, my problem/what I can't work out is how to send a message from the onReceive method of a receiver to an activity. Lets say I have a receiver as such:

public class ReceiveMessages extends BroadcastReceiver 
{
@Override
   public void onReceive(Context context, Intent intent) 
   {    
       String action = intent.getAction();
       if(action.equalsIgnoreCase(TheService.DOWNLOADED)){    
           // send message to activity
       }
   }
}

How would I send a message to an activity?

Would I have to instantiate the receiver in the activity I want to send messages to and monitor it somehow? Or what? I understand the concept, but not really the application.

Any help would be absolutely amazing, thank you.

Tom

Thomas Clayson
  • 29,657
  • 26
  • 147
  • 224

4 Answers4

79

EDITED Corrected code examples for registering/unregistering the BroadcastReceiver and also removed manifest declaration.

Define ReceiveMessages as an inner class within the Activity which needs to listen for messages from the Service.

Then, declare class variables such as...

ReceiveMessages myReceiver = null;
Boolean myReceiverIsRegistered = false;

In onCreate() use myReceiver = new ReceiveMessages();

Then in onResume()...

if (!myReceiverIsRegistered) {
    registerReceiver(myReceiver, new IntentFilter("com.mycompany.myapp.SOME_MESSAGE"));
    myReceiverIsRegistered = true;
}

...and in onPause()...

if (myReceiverIsRegistered) {
    unregisterReceiver(myReceiver);
    myReceiverIsRegistered = false;
}

In the Service create and broadcast the Intent...

Intent i = new Intent("com.mycompany.myapp.SOME_MESSAGE");
sendBroadcast(i);

And that's about it. Make the 'action' unique to your package / app, i.e., com.mycompany... as in my example. This helps avoiding a situation where other apps or system components might attempt to process it.

micha
  • 47,774
  • 16
  • 73
  • 80
Squonk
  • 48,735
  • 19
  • 103
  • 135
  • "This prevents other apps or system components from processing it." -- not really. `setPackage()` behaves this way, though that's relatively new to `Intent`. Otherwise, anyone can listen on custom actions, as they are just strings. There's a bit of security-by-obscurity, and using custom action strings largely eliminates *accidental* cross-app chatter. – CommonsWare Sep 02 '11 at 11:29
  • @CommonsWare: "There's a bit of security-by-obscurity, and using custom action strings largely eliminates accidental cross-app chatter" - sorry, I understand all the words you said there but your sentence makes no sense. Perhaps I should have said "...might help to prevent...". Also in your answer you say "No offense, but your question is still damn vague."....it might help if you'd followed Thomas's original question which lead to this one. I did and I have a basic handle on what Thomas is looking for. Not saying I'm right but I took the time to follow some threads. – Squonk Sep 02 '11 at 13:12
  • 1
    "it might help if you'd followed Thomas's original question" -- if Thomas wanted people to read the original question, Thomas would have linked to it. – CommonsWare Sep 02 '11 at 13:18
  • That's exactly what I wanted thank you MisterSqonk. Just to clear things up - my last question was a bit obscure and I wanted to start fresh with a new question that did follow on from the last one, but I thought linking to my last question would just confuse matters. So thank you very much MisterSquonk because you've managed to understand across threads. :) – Thomas Clayson Sep 04 '11 at 12:48
  • @Thomas: Glad to help - I appreciate you didn't want to link back but I sometimes check other threads just to get an angle on what people are looking for. Good luck. :) – Squonk Sep 05 '11 at 03:11
  • So I have almost this exact code. I have an Activity1 that creates a local Service1, then changes to Activity2. Activity2 registers a receiver as above. Service1 sends a broadcast as above, but Activity2 never gets it. Any idea why? (I don't have anything in the manifest regarding the intent filter, as I'm registering the receiver manually.) – Garret Wilson Nov 12 '11 at 23:45
  • @Garret: I'd suggest posting your problem as a new SO question with some snippets of code - it's difficult to tell without seeing code examples. – Squonk Nov 12 '11 at 23:54
  • @MisterSquonk: OK, I'll try to post a question tomorrow. Since the example is so small (and so simple, you'd think) I just wondered if there was a simple trick I was forgetting---the code is basically identical. Thanks, anyway. – Garret Wilson Nov 13 '11 at 00:01
  • @Garret: No, sorry I can't think of any obvious 'gotchas'. It really should be as simple as the code in my answer. I'll look out for the question tomorrow. – Squonk Nov 13 '11 at 00:08
  • @MisterSquonk: Here you go: http://stackoverflow.com/questions/8112164/android-activity-not-getting-broadcast-from-local-service Many thanks in advance. – Garret Wilson Nov 13 '11 at 14:20
73

No offense, but your question is still damn vague. So, I'm going to outline a whole mess of scenarios and hope that one of them actually hits whatever problem you think you have.

Scenario A: Only The Activity

If you only need to receive the broadcast when you have an activity in the foreground, have the activity register the BroadcastReceiver using registerReceiver(). As @MisterSquonk indicated, you would register the receiver in onResume() and unregister it in onPause().

Scenario B: Activity If In Foreground, Else Other; Ordered Broadcast

If you want the foreground activity to handle the broadcast, but you want something else to happen if that activity is not in the foreground (e.g., raise a Notification), and the broadcast is an ordered broadcast (e.g., incoming SMS), then you would still use the Scenario A solution, but with a higher-priority IntentFilter (see setPriority()). In addition, you would register a BroadcastReceiver via a <receiver> element in the manifest, with a lower-priority <intent-filter> for the same broadcast. In the activity's BroadcastReceiver, call abortBroadcast() to consume the event and prevent it from reaching your manifest-registered BroadcastReceiver.

Scenario C: Activity If In Foreground, Else Other; Regular Broadcast

If Scenario B almost fits, but the broadcast you are listening for is not an ordered broadcast, you will need to start with Scenario B. However, have the broadcast that both receivers have in their respective filters be one of your own, using a private action string as @MisterSquonk suggested. In addition, have another BroadcastReceiver registered in the manifest, whose <intent-filter> is for the real broadcast you're listening for. That receiver would simply call sendOrderedBroadcast() to send out the ordered broadcast that the other receivers are listening on.

Scenario D: Activity Regardless of Foreground

If some activity of yours needs to know about the broadcast, and it does not matter whether or not it is in the foreground, you need to rethink what you mean by that. Usually, this really means that the broadcast affects your data model in some way, in which case your concern should not be to let the activities know, but rather to update your data model, and use your already-existing "let the activities know about the data model change" logic handle the rest.

If, however, you are convinced that this is not part of your data model, you can implement Scenario B or Scenario C, plus stick some information in a static data member. Your activities can examine that static data member in onResume() to pick up the information about the broadcast when they return to the foreground.

If you're thinking "but, what if my process is terminated between the broadcast and the other activity coming to the foreground?", then your broadcast really is updating your data model, per the opening paragraph of this scenario.

If you're thinking "but, I want to update an activity that is doing work in the background", then the activity in question is broken. Activities should never be doing work in the background. That work should be delegated to some form of service, and there's a whole related set of scenarios for getting a broadcast to the service.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 5
    This is all very interesting and very useful. Answer tick went to MisterSquonk because he provided code and managed to solve the acctual issue I was having. However this is extremely good information to have. Thank you. – Thomas Clayson Sep 04 '11 at 12:51
21

To broadcast an intent:

Intent intent = new Intent("com.yourcompany.testIntent");
intent.putExtra("value","test");
sendBroadcast(intent);

To receive the same intent use:

IntentFilter filter = new IntentFilter("com.yourcompany.testIntent");
        BroadcastReceiver receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
              String value =  intent.getExtras().getString("value");
            }
        };
registerReceiver(receiver, filter);
AlexA
  • 271
  • 2
  • 5
9

Possibly not relevant at the time of the question being asked but there is now the LocalBroadcastManager in the Android Support Package.

Works pretty much the same way as normal broadcasts but all "chatter" is local to the app it is running in.

Advantages:

  • You know that the data you are broadcasting won't leave your app, so don't need to worry about leaking private data.
  • It is not possible for other applications to send these broadcasts to your app, so you don't need to worry about having security holes they can exploit.
  • It is more efficient than sending a global broadcast through the system.

Example:

Intent i = new Intent("my.local.intent");
LocalBroadcastManager.getInstance(context).sendBroadcast(i);

and to receive

receiver = new MyBroadcastReceiverToHandleLocalBroadcast();

IntentFilter i = new IntentFilter();
i.addAction("my.local.intent");
LocalBroadcastManager.getInstance(context).registerReceiver(receiver, i);
Kuffs
  • 35,581
  • 10
  • 79
  • 92
  • 1
    And now, it's deprecated: https://developer.android.com/jetpack/androidx/releases/localbroadcastmanager?hl=en – xuiqzy Oct 29 '20 at 10:07