Most of us are all too familiar with announcements in public places. They are loud messages that are meant to inform large groups of people about something important. Android applications also have to deal with announcements sometimes—announcements generated either by other applications or by the Android operating system itself. Such announcements are called broadcasts, and they are seen as an important form of asynchronous inter-process communication on the Android platform.
In this tutorial, I'll show you how to create, send and receive both local and system-wide broadcasts. I'll also show you how to use a third-party library called EventBus that can serve as an alternative to local broadcasts.
1. Creating a Broadcast
Android broadcasts are sent in the form of Intent
objects. Therefore, before you create a broadcast, you must create an Intent
object. This series has a detailed tutorial about intents. If you haven't read it yet, now would be a good time to do so.
Every intent you send as a broadcast must have an action name. Although the action name can be any string, I recommend that you always prefix it with the package name of your app in order to avoid conflicts with broadcasts of other applications.
The follow code snippet shows you how to create a new intent whose action name is com.tutsplus.my.first.broadcast:
Intent myIntent = new Intent("com.tutsplus.my.first.broadcast");
To send the intent as a broadcast, all you need to do is call the sendBroadcast()
method and pass the Intent
object as an argument to it.
sendBroadcast(myIntent);
Note that the sendBroadcast()
method creates a global broadcast that can be received by not only your application but also any other application installed on the user's device.
2. Receiving Broadcasts
To be able to receive a broadcast, your application must have an appropriately configured broadcast receiver. You can create a broadcast receiver by extending the BroadcastReceiver
abstract class and overriding its onReceive()
method. For example, here's how you create a broadcast receiver that prints a message every time it receives a broadcast:
public class MyBroadcastReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { Log.d("MYAPP", "I received a broadcast"); } }
A broadcast receiver works only if it is registered. The easiest way to do so is to declare it in the project's manifest file using a receiver
tag.
Additionally, inside the declaration of a broadcast receiver, you must include intent-filter
tags that specify the action names the broadcast receiver is interested in.
The following code snippet registers MyBroadcastReceiver
and configures it to respond to an action called com.tutsplus.my.first.broadcast:
<receiver android:name=".MyBroadcastReceiver"> <intent-filter> <action android:name="com.tutsplus.my.first.broadcast" /> </intent-filter> </receiver>
3. Receiving System Broadcasts
The Android operating system issues several global broadcasts. Most of them contain valuable information about changes in the Android device's state. For example, every time a user places a new outgoing call, the android.intent.action.NEW_OUTGOING_CALL
broadcast is issued. Similarly, every time a user turns airplane mode on or off, the android.intent.action.AIRPLANE_MODE
broadcast is issued.
Receiving system broadcasts is just like receiving ordinary broadcasts. Most system broadcast intents, however, contain additional information in the form of extras. You can fetch all those extras as a Bundle
object by calling the getExtras()
method. For example, the android.intent.action.AIRPLANE_MODE
broadcast intent always contains an extra called state
, which is a boolean specifying whether airplane mode has been turned on or off.
The following code snippet shows you how to log the value of the state
extra:
// Check if you have the right broadcast intent if (intent.getAction().equals("android.intent.action.AIRPLANE_MODE")) { // Get all extras Bundle extras = intent.getExtras(); // Fetch the boolean extra using getBoolean() boolean state = extras.getBoolean("state"); // Log the value of the extra Log.d("MYAPP", "AIRPLANE MODE: " + state); }
Note that the above code will work only if the broadcast receiver has been registered with the following intent filter:
<intent-filter> <action android:name="android.intent.action.AIRPLANE_MODE" /> </intent-filter>
4. Working With Local Broadcasts
For obvious reasons, global broadcasts must never contain sensitive information. You can, however, broadcast such information locally using the LocalBroadcastManager
class, which is a part of the Android Support Library.
If you are using the latest version of Android Studio, you won't have to manually add a dependency for the Android Support Library. However, if you want to implement local broadcasts in an old project, make sure that the following line is present in the app module's build.gradle file:
compile 'com.android.support:support-v4:23.4.0'
To create a new instance of the LocalBroadcastManager
class, you must call its getInstance()
method and pass your activity or service as its context. For example, here's how you would create the instance inside an activity called MyActivity
:
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(MyActivity.this);
You can now send local broadcasts using the sendBroadcast()
method of the LocalBroadcastManager
class. The following code snippet creates a new intent, whose action name is my-local-broadcast
, and sends it as a local broadcast:
// Create intent with action Intent myIntent = new Intent("my-local-broadcast"); // Send local broadcast localBroadcastManager.sendBroadcast(myIntent);
Receiving a local broadcast is slightly more involved. A local broadcast receiver must not be registered in the project's manifest file. Instead, it must be registered dynamically using the registerReceiver()
method of the LocalBroadcastManager
class. In addition to a broadcast receiver instance, the registerReceiver()
method requires an IntentFilter
object specifying the action name the broadcast receiver should respond to.
Here's how you would create and register a broadcast receiver that can respond to the my-local-broadcast
action:
// Create a broadcast receiver as usual BroadcastReceiver myBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.d("MYAPP", "Received a local broadcast"); } }; // Create intent filter for it IntentFilter myFilter = new IntentFilter("my-local-broadcast"); // Register it localBroadcastManager.registerReceiver( myBroadcastReceiver, myFilter);
Dynamically registered receivers must be unregistered when they are no longer necessary. To do so, you must use the unregisterReceiver()
method of the LocalBroadcastManager
class.
localBroadcastManager.unregisterReceiver( myBroadcastReceiver);
5. Using EventBus
If you think local broadcasts and intents don't give you all the flexibility you need, you should consider using EventBus, a third-party library that allows you to asynchronously send data from one component of your application to another. In my opinion, the EventBus API is very intuitive and succinct.
To include EventBus in your project, add the following compile
dependency to your app module's build.gradle file:
compile 'org.greenrobot:eventbus:3.0.0'
Instead of just Intent
objects, EventBus allows you to send and receive objects of any Java class. For example, instances of the following class can be easily used with EventBus:
public class MyMessage { String title; String text; public MyMessage(String title, String text) { this.title = title; this.text = text; } }
EventBus implements the publisher-subscriber pattern and creates a central bus that is accessible to all the components of your application. Therefore, a component that needs to send objects must simply publish the objects on the bus. A component that is interested in receiving those objects must register itself as a subscriber of the bus.
The quickest way to fetch an instance of the central bus is to use the getDefault()
method of the EventBus
class.
EventBus bus = EventBus.getDefault();
To publish an object, all you need to do is call the post()
method of the bus. For example, here's how you would publish an instance of the MyMessage
class:
bus.post(new MyMessage("Hello", "My first EventBus message"));
An app component, such as an activity or service, can register itself as a subscriber by calling the register()
method of the bus. The following sample code registers a service called MyService
:
bus.register(MyService.this);
A component that is acting as a subscriber must also have a subscriber method, which is any method that has the @Subscribe
annotation. Subscriber methods behave much like event handlers and are automatically called every time new objects are available on the bus.
The following code snippet shows you how to create a very simple subscriber method that is automatically called every time a new instance of the MyMessage
class is available on the bus:
@Subscribe public void messageAvailable(MyMessage message) { Log.d("MYAPP", message.title + " [" + message.text + "]"); }
Conclusion
In this tutorial, you learned how to send information from one application component to another using both global and local broadcasts. You also learned how to use the very popular EventBus library for intra-app communication. Because broadcasts have a very low overhead, you are free to send them as often as necessary. In fact, the Android system generates a new android.intent.action.TIME_TICK
broadcast every single minute.
To learn more about Android broadcast receivers and broadcasts, you can refer to the documentation of the BroadcastReceiver and Intent classes.
Comments