7.3 Broadcasts
Contents:
- Introduction
- Broadcasts
- Broadcast receivers
- Restricting broadcasts
- Best practices
- Related practical
- Learn more
In this chapter you learn about broadcasts and broadcast receivers. Broadcasts are messaging components used for communicating across different apps, and also with the Android system, when an event of interest occurs. Broadcast receivers are the components in your Android app that listen for these events and respond accordingly.
Broadcasts
Broadcasts are messages that the Android system and Android apps send when events occur that might affect the functionality of other apps. For example, the Android system sends an event when the system boots up, when power is connected or disconnected, and when headphones are connected or disconnected. Your Android app can also broadcast events, for example when new data is downloaded.
In general, broadcasts are messaging components used for communicating across apps when events of interest occur. There are two types of broadcasts:
- System broadcasts are delivered by the system.
- Custom broadcasts are delivered by your app.
System broadcasts
A system broadcast is a message that the Android system sends when a system event occurs. System broadcasts are wrapped in Intent
objects. The intent object's action field contains event details such as android.intent.action.HEADSET_PLUG
, which is sent when a wired headset is connected or disconnected. The intent can also contain more data about the event in its extra field, for example a boolean
extra indicating whether a headset is connected or disconnected.
Examples:
- When the device boots, the system broadcasts a system
Intent
with the actionACTION_BOOT_COMPLETED
. - When the device is disconnected from external power, the system sends a system
Intent
with the action fieldACTION_POWER_DISCONNECTED
.
System broadcasts aren't targeted at specific recipients. Interested apps must register a component to "listen" for these events. This listening component is called a broadcast receiver.
As the Android system evolves, significant changes are made to improve the system's performance. For example, starting from Android 7.0, the system broadcast actions ACTION_NEW_PICTURE
and ACTION_NEW_VIDEO
are not supported. Apps can no longer receive broadcasts about these actions, regardless of the targetSDK
version on the device. This is because device cameras take pictures and record videos frequently, so sending a system broadcast every time one of these actions occurred would strain a device's memory and battery.
To get the complete list of broadcast actions that the system can send for a particular SDK version, check the broadcast_actions.txt
file in your SDK folder, at the following path: Android/sdk/platforms/android-
xx
/data,
where xx
is the SDK version.
Custom broadcasts
Custom broadcasts are broadcasts that your app sends out. Use a custom broadcast when you want your app to take an action without launching an activity. For example, use a custom broadcast when you want to let other apps know that data has been downloaded to the device and is available for them to use. More than one broadcast receiver can be registered to receive your broadcast.
To create a custom broadcast, define a custom Intent
action.
Intent
, use your unique package name (for example com.example.myproject
) to make sure that your intent doesn't conflict with an intent that is broadcast from a different app or from the Android system.
There are three ways to deliver a custom broadcast:
- For a normal broadcast, pass the intent to
sendBroadcast()
. - For an ordered broadcast, pass the intent to
sendOrderedBroadcast()
. - For a local broadcast, pass the intent to
LocalBroadcastManager.sendBroadcas
t()
.
Normal, ordered, and local broadcasts are described in more detail below.
Normal broadcasts
The sendBroadcast()
method sends broadcasts to all the registered receivers at the same time, in an undefined order. This is called a normal broadcast. A normal broadcast is the most efficient way to send a broadcast. With normal broadcasts, receivers can't propagate the results among themselves, and they can't cancel the broadcast.
The following method sends a normal broadcast to all interested broadcast receivers:
public void sendBroadcast() {
Intent intent = new Intent();
intent.setAction("com.example.myproject.ACTION_SHOW_TOAST");
// Set the optional additional information in extra field.
intent.putExtra("data","This is a normal broadcast");
sendBroadcast(intent);
}
Ordered broadcasts
To send a broadcast to one receiver at a time, use the sendOrderedBroadcast()
method:
- The
android:priority
attribute that's specified in the intent filter determines the order in which the broadcast is sent. - If more than one receiver with same priority is present, the sending order is random.
- The
Intent
is propagated from one receiver to the next. - During its turn, a receiver can update the
Intent
, or it can cancel the broadcast. (If the receiver cancels the broadcast, theIntent
can't be propagated further.)
For example, the following method sends an ordered broadcast to all interested broadcast receivers:
public void sendOrderedBroadcast() {
Intent intent = new Intent();
// Set a unique action string prefixed by your app package name.
intent.setAction("com.example.myproject.ACTION_NOTIFY");
// Deliver the Intent.
sendOrderedBroadcast(intent);
}
Local broadcasts
If you don't need to send broadcasts to a different app, use the LocalBroadcastManager.sendBroadcas
t()
method, which sends broadcasts to receivers within your app. This method is efficient, because it doesn't involve interprocess communication. Also, using local broadcasts protects your app against some security issues.
To send a local broadcast:
- To get an instance of
LocalBroadcastManager
, callgetInstance()
and pass in the application context. - Call
sendBroadcast()
on the instance. Pass in the intent that you want to broadcast.LocalBroadcastManager.getInstance(this).sendBroadcast(customBroadcastIntent);
Broadcast receivers
Broadcast receivers are app components that can register for system events or app events. When an event occurs, registered broadcast receivers are notified via an Intent
. For instance, if you are implementing a media app and you're interested in knowing when the user connects or disconnects a headset, register for the ACTION_HEADSET_PLUG
intent action.
Use broadcast receivers to respond to messages that have been broadcast from apps or from the Android system. To create a broadcast receiver:
- Define a subclass of the
BroadcastReceiver
class and implement theonReceive()
method. - Register the broadcast receiver, either statically or dynamically.
These steps are described below.
Subclass a BroadcastReceiver
To create a broadcast receiver, define a subclass of the BroadcastReceiver
class. This subclass is where Intent
objects are delivered if they match the intent filters you register for.
Within your subclass:
- Implement the
onReceive()
method, which is called when theBroadcastReceiver
object receives anIntent
broadcast . - Inside
onReceive()
, include any other logic that your broadcast receiver needs.
Example: Create a broadcast receiver
In this example, the myReceiver
class a subclass of BroadcastReceiver
. If the incoming broadcast intent has the ACTION_SHOW_TOAST
action, the myReceiver
class shows a toast message:
//Subclass of the BroadcastReceiver class.
private class myReceiver extends BroadcastReceiver {
// Override the onReceive method to receive the broadcasts
@Override
public void onReceive(Context context, Intent intent) {
//Check the Intent action and perform the required operation
if (intent.getAction().equals(ACTION_SHOW_TOAST)) {
CharSequence text = "Broadcast Received!";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
}
}
The onReceive()
method is called when your app receives a registered Intent
broadcast. The onReceive()
method runs on the main thread unless it is explicitly asked to run on a different thread in the registerReceiver()
method.
The onReceive()
method has a timeout of 10 seconds. After 10 seconds, the Android system considers your receiver to be blocked, and the system might show the user an "application not responding" error. For this reason, you shouldn't implement long-running operations in onReceive()
.
onReceive()
implementation, because once your code returns from onReceive()
, the system considers the BroadcastReceiver
component to be finished. If onReceive()
started an asynchronous operation, the system would stop the BroadcastReceiver
process before the asynchronous operation had a chance to complete.
In particular:
- Don't try to show a dialog from within a
BroadcastReceiver
. Instead, display a notification using theNotificationManager
API. - Don't try to bind to a service from within a
BroadcastReceiver
. Instead, useContext.startService()
to send a command to the service.
If you need to perform a long-running operation inside BroadcastReceiver
, use WorkManager
to schedule a job. When you schedule a task with WorkManager
, the task is guaranteed to run. WorkManager
chooses the appropriate way to run your task, based on such factors as the device API level and the app state.
Register your broadcast receiver and set intent filters
There are two types of broadcast receivers:
- Static receivers, which you register in the Android manifest file.
- Dynamic receivers, which you register using a context.
Static receivers
Static receivers are also called manifest-declared receivers. To register a static receiver, include the following attributes inside the <receiver>
element in your AndroidManifest.xml
file:
-
The value for this attribute is the fully classified name of the
BroadcastReceiver
subclass, including the package name. To use the package name that's specified in the manifest, prefix the subclass name with a period, for example.AlarmReceiver
. android:exported
(optional)If this boolean value is set to
false
, other apps cannot send broadcasts to your receiver. This attribute is important for security.-
Include this nested element to specify the broadcast
Intent
actions that your broadcast receiver component is listening for.
The following code snippet shows static registration of a broadcast receiver that listens for a custom broadcast Intent
with the action "ACTION_SHOW_TOAST
":
- The receiver's
name
is the name of theBroadcastReceiver
subclass (.AlarmReceiver
). - The receiver is not exported, meaning that no other apps can deliver broadcasts to this app.
- The intent filter checks whether incoming intents include an action named
ACTION_SHOW_TOAST
, which is a customIntent
action defined within the app.<receiver android:name=".AlarmReceiver" android:exported="false"> <intent-filter> <action android:name= "com.example.myproject.intent.action.ACTION_SHOW_TOAST"/> </intent-filter> </receiver>
Note: For Android 8.0 (API level 26) and higher, static receivers can't receive most implicit broadcasts. (Implicit broadcasts are broadcasts that don't target your app specifically.) Even if you register for these broadcasts in the manifest, the Android system won't deliver them to your app. However, you can still use a dynamic receiver to register for these broadcasts.
A few broadcasts, such as ACTION_BOOT_COMPLETED
and ACTION_TIMEZONE_CHANGED
, are excepted from this restriction. You can declare them in the manifest, no matter what the target API level. To learn more, see the complete list of implicit broadcast exceptions.
</div>
Intent filters
An intent filter specifies the types of intents that a component can receive. When the system receives an Intent
as a broadcast, it searches the broadcast receivers based on the values specified in receivers' intent filters.
To create an intent filter statically, use the <intent-filter>
element in the Android manifest. An <intent-filter>
element has one required element, <action>
. The Android system compares the Intent
action of the incoming broadcast to the filter action's android:name
strings. If any of the names in the filter match the action name in the incoming broadcast, the broadcast is sent to your app.
This <intent-filter>
listens for a system broadcast that's sent when the device boots up. Only Intent
objects with an action named BOOT_COMPLETED
match the filter:
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
If no intent filters are specified, the broadcast receiver can only be activated with an explicit broadcast Intent
that specifies the component by name. (This is similar to how explicit intents are used to launch activities by their class names.)
To learn more about using intent filters, see the Intent resolution section of the Intent
guide.
Dynamic receivers
Dynamic receivers are also called context-registered receivers. You register a dynamic receiver using an application context or an Activity
context. A dynamic receiver receives broadcasts as long as the registering context is valid:
- If you use the application context to register your receiver, your app receives relevant broadcasts as long as your app is running in either the foreground or the background.
- If you use an
Activity
context to register your receiver, your app receives relevant broadcasts until thatActivity
is destroyed.
To use the context to register your receiver dynamically:
Create an
IntentFilter
and add theIntent
actions that you want your app to listen for. You can add more than one action to the sameIntentFilter
object.IntentFilter intentFilter = new IntentFilter(); filter.addAction(Intent.ACTION_POWER_CONNECTED); filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
When the device power is connected or disconnected, the Android system broadcasts the
Intent.ACTION_POWER_CONNECTED
andIntent.ACTION_POWER_DISCONNECTED
intent actions.Register the receiver by calling
registerReceiver()
method on the context. Pass in theBroadcastReceiver
object and theIntentFilter
object.mReceiver = new AlarmReceiver(); this.registerReceiver(mReceiver, intentFilter);
In this example the
Activity
context (this
) is used to register your receiver. So the app will receive the broadcast as long as theActivity
is running.
Local broadcasts
You must register local receivers dynamically, because static registration in the manifest is not possible for a local broadcasts.
To register a receiver for local broadcasts:
- Get an instance of
LocalBroadcastManager
by calling thegetInstance()
method. - Call
registerReceiver()
, passing in the receiver and anIntentFilter
object.LocalBroadcastManager.getInstance(this) .registerReceiver(mReceiver, new IntentFilter(CustomReceiver.ACTION_CUSTOM_BROADCAST));
Unregister the receiver
To save system resources and avoid leaks, unregister dynamic receivers when your app no longer needs them, or before the Activity
or app is destroyed. This is also true for local broadcast receivers, because they're registered dynamically.
To unregister a normal broadcast receiver:
Call
unregisterReceiver()
and pass in yourBroadcastReceiver
object:unregisterReceiver(mReceiver);
To unregister a local broadcast receiver:
Get an instance of the
LocalBroadcastManager
.Call
LocalBroadcastManager.unregisterReceiver()
and pass in yourBroadcastReceiver
object:LocalBroadcastManager.getInstance(this) .unregisterReceiver(mReceiver);
Where you call these
unregisterReceiver()
methods depends on the desired lifecycle of yourBroadcastReceiver
object:Sometimes the receiver is only needed when your activity is visible, for example to disable a network function when the network is not available. In these cases, register the receiver in
onResume()
and unregister the receiver inonPause()
.- You can also use the
onStart()
/onStop()
oronCreate()
/onDestroy()
method pairs, if they are more appropriate for your use case.
Restricting broadcasts
An unrestricted broadcast can pose a security threat, because any registered receiver can receive it. For example, if your app uses a normal broadcast to send an implicit Intent
that includes sensitive information, an app that contains malware could receive that broadcast. Restricting your broadcast is strongly recommended.
Ways to restrict a broadcast:
- If possible, use a
LocalBroadcastManager
, which keeps the data inside your app, avoiding any security leaks. You can only useLocalBroadcastManager
if you don't need interprocess communication or communication with other apps. - Use the
setPackage()
method and pass in the package name. Your broadcast is restricted to apps that match the specified package name. - Enforce access permissions on the sender side, on the receiver side, or both.
To enforce a permission when sending a broadcast:
- Supply a non-null permission argument to
sendBroadcast()
. Only receivers that request this permission using the<uses-permission>
tag in theirAndroidManifest.xml
file can receive the broadcast.
To enforce a permission when receiving a broadcast:
- If you register your receiver dynamically, supply a non-null permission to
registerReceiver()
. - If you register your receiver statically, use the
android:permission
attribute inside the<receiver>
tag in yourAndroidManifest.xml
.
Best practices
- In your intent actions, prefix
String
constants with your app's package name. Otherwise, you may conflict with other apps' intents. TheIntent
namespace is global. - Restrict broadcast receivers, as described above.
- Prefer dynamic receivers over static receivers.
- Do not start an
Activity
from a broadcast receiver—use a notification instead. Starting an activity from a broadcast receiver causes a bad user experience if more than one receiver is listening for the same broadcast event. - Never perform a long running operation in the broadcast receiver's
onReceive(Context, Intent)
method, because the method runs on the main UI thread. Consider using aJobScheduler
or aWorkManager
instead.
Related practical
The related practical is 7.3: Broadcast receivers.