Android apps can send or receive broadcast messages from the Android system and other Android apps, similar to the publish-subscribe design pattern. These broadcasts are sent when an event of interest occurs. For example, the Android system sends broadcasts when various system events occur, such as when the system boots up or the device starts charging. Apps can also send custom broadcasts, for example, to notify other apps of something that they might be interested in (for example, some new data has been downloaded).

Apps can register to receive specific broadcasts. When a broadcast is sent, the system automatically routes broadcasts to apps that have subscribed to receive that particular type of broadcast.

Generally speaking, broadcasts can be used as a messaging system across apps and outside of the normal user flow. However, you must be careful not to abuse the opportunity to respond to broadcasts and run jobs in the background that can contribute to a slow system performance, as described in the following video.

About system broadcasts

The system automatically sends broadcasts when various system events occur, such as when the system switches in and out of airplane mode. System broadcasts are sent to all apps that are subscribed to receive the event.

The broadcast message itself is wrapped in an Intent object whose action string identifies the event that occurred (for example android.intent.action.AIRPLANE_MODE ). The intent may also include additional information bundled into its extra field. For example, the airplane mode intent includes a boolean extra that indicates whether or not Airplane Mode is on.

For more information about how to read intents and get the action string from an intent, see Intents and Intent Filters.

For a complete list of system broadcast actions, see the BROADCAST_ACTIONS.TXT file in the Android SDK. Each broadcast action has a constant field associated with it. For example, the value of the constant ACTION_AIRPLANE_MODE_CHANGED is android.intent.action.AIRPLANE_MODE . Documentation for each broadcast action is available in its associated constant field.

Changes to system broadcasts

As the Android platform evolves, it periodically changes how system broadcasts behave. Keep the following changes in mind if your app targets Android 7.0 (API level 24) or higher, or if it's installed on devices running Android 7.0 or higher.

Android 9

Beginning with Android 9 (API level 28), The NETWORK_STATE_CHANGED_ACTION broadcast doesn't receive information about the user's location or personally identifiable data.

In addition, if your app is installed on a device running Android 9 or higher, system broadcasts from Wi-Fi don't contain SSIDs, BSSIDs, connection information, or scan results. To get this information, call getConnectionInfo() instead.

Android 8.0

Beginning with Android 8.0 (API level 26), the system imposes additional restrictions on manifest-declared receivers.

If your app targets Android 8.0 or higher, you cannot use the manifest to declare a receiver for most implicit broadcasts (broadcasts that don't target your app specifically). You can still use a context-registered receiver when the user is actively using your app.

Android 7.0

Android 7.0 (API level 24) and higher don't send the following system broadcasts:

Also, apps targeting Android 7.0 and higher must register the CONNECTIVITY_ACTION broadcast using registerReceiver(BroadcastReceiver, IntentFilter) . Declaring a receiver in the manifest doesn't work.

Receiving broadcasts

Apps can receive broadcasts in two ways: through manifest-declared receivers and context-registered receivers.

Manifest-declared receivers

If you declare a broadcast receiver in your manifest, the system launches your app (if the app is not already running) when the broadcast is sent.

Note: If your app targets API level 26 or higher, you cannot use the manifest to declare a receiver for implicit broadcasts (broadcasts that do not target your app specifically), except for a few implicit broadcasts that are exempted from that restriction . In most cases, you can use scheduled jobs instead.

To declare a broadcast receiver in the manifest, perform the following steps:

Specify the <receiver> element in your app's manifest. <receiver android:name=".MyBroadcastReceiver" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.INPUT_METHOD_CHANGED" /> </intent-filter> </receiver> The intent filters specify the broadcast actions your receiver subscribes to. Subclass BroadcastReceiver and implement onReceive(Context, Intent) . The broadcast receiver in the following example logs and displays the contents of the broadcast: Kotlin private const val TAG = "MyBroadcastReceiver" class MyBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { StringBuilder().apply { append("Action: ${intent.action}

") append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}

") toString().also { log -> Log.d(TAG, log) Toast.makeText(context, log, Toast.LENGTH_LONG).show() } } } } Java public class MyBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "MyBroadcastReceiver"; @Override public void onReceive(Context context, Intent intent) { StringBuilder sb = new StringBuilder(); sb.append("Action: " + intent.getAction() + "

"); sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "

"); String log = sb.toString(); Log.d(TAG, log); Toast.makeText(context, log, Toast.LENGTH_LONG).show(); } }

The system package manager registers the receiver when the app is installed. The receiver then becomes a separate entry point into your app which means that the system can start the app and deliver the broadcast if the app is not currently running.

The system creates a new BroadcastReceiver component object to handle each broadcast that it receives. This object is valid only for the duration of the call to onReceive(Context, Intent) . Once your code returns from this method, the system considers the component no longer active.

Context-registered receivers

To register a receiver with a context, perform the following steps:

Create an instance of BroadcastReceiver . Kotlin val br: BroadcastReceiver = MyBroadcastReceiver() Java BroadcastReceiver br = new MyBroadcastReceiver(); Create an IntentFilter and register the receiver by calling registerReceiver(BroadcastReceiver, IntentFilter) : Kotlin val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION).apply { addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED) } registerReceiver(br, filter) Java IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); this.registerReceiver(br, filter); Note: LocalBroadcastManager.registerReceiver(BroadcastReceiver, IntentFilter) instead. To register for local broadcasts, callinstead. Context-registered receivers receive broadcasts as long as their registering context is valid. For an example, if you register within an Activity context, you receive broadcasts as long as the activity is not destroyed. If you register with the Application context, you receive broadcasts as long as the app is running. To stop receiving broadcasts, call unregisterReceiver(android.content.BroadcastReceiver) . Be sure to unregister the receiver when you no longer need it or the context is no longer valid. Be mindful of where you register and unregister the receiver, for example, if you register a receiver in onCreate(Bundle) using the activity's context, you should unregister it in onDestroy() to prevent leaking the receiver out of the activity context. If you register a receiver in onResume() , you should unregister it in onPause() to prevent registering it multiple times (If you don't want to receive broadcasts when paused, and this can cut down on unnecessary system overhead). Do not unregister in onSaveInstanceState(Bundle) , because this isn't called if the user moves back in the history stack.

Effects on process state

The state of your BroadcastReceiver (whether it is running or not) affects the state of its containing process, which can in turn affect its likelihood of being killed by the system. For example, when a process executes a receiver (that is, currently running the code in its onReceive() method), it is considered to be a foreground process. The system keeps the process running except under cases of extreme memory pressure.

However, once your code returns from onReceive() , the BroadcastReceiver is no longer active. The receiver's host process becomes only as important as the other app components that are running in it. If that process hosts only a manifest-declared receiver (a common case for apps that the user has never or not recently interacted with), then upon returning from onReceive() , the system considers its process to be a low-priority process and may kill it to make resources available for other more important processes.

For this reason, you should not start long running background threads from a broadcast receiver. After onReceive() , the system can kill the process at any time to reclaim memory, and in doing so, it terminates the spawned thread running in the process. To avoid this, you should either call goAsync() (if you want a little more time to process the broadcast in a background thread) or schedule a JobService from the receiver using the JobScheduler , so the system knows that the process continues to perform active work. For more information, see Processes and Application Life Cycle.

The following snippet shows a BroadcastReceiver that uses goAsync() to flag that it needs more time to finish after onReceive() is complete. This is especially useful if the work you want to complete in your onReceive() is long enough to cause the UI thread to miss a frame (>16ms), making it better suited for a background thread.

Kotlin private const val TAG = "MyBroadcastReceiver" class MyBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val pendingResult: PendingResult = goAsync() val asyncTask = Task(pendingResult, intent) asyncTask.execute() } private class Task( private val pendingResult: PendingResult, private val intent: Intent ) : AsyncTask<String, Int, String>() { override fun doInBackground(vararg params: String?): String { val sb = StringBuilder() sb.append("Action: ${intent.action}

") sb.append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}

") return toString().also { log -> Log.d(TAG, log) } } override fun onPostExecute(result: String?) { super.onPostExecute(result) // Must call finish() so the BroadcastReceiver can be recycled. pendingResult.finish() } } } Java public class MyBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "MyBroadcastReceiver"; @Override public void onReceive(Context context, Intent intent) { final PendingResult pendingResult = goAsync(); Task asyncTask = new Task(pendingResult, intent); asyncTask.execute(); } private static class Task extends AsyncTask<String, Integer, String> { private final PendingResult pendingResult; private final Intent intent; private Task(PendingResult pendingResult, Intent intent) { this.pendingResult = pendingResult; this.intent = intent; } @Override protected String doInBackground(String... strings) { StringBuilder sb = new StringBuilder(); sb.append("Action: " + intent.getAction() + "

"); sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "

"); String log = sb.toString(); Log.d(TAG, log); return log; } @Override protected void onPostExecute(String s) { super.onPostExecute(s); // Must call finish() so the BroadcastReceiver can be recycled. pendingResult.finish(); } } }

Sending broadcasts

Android provides three ways for apps to send broadcast:

The sendOrderedBroadcast(Intent, String) method sends broadcasts to one receiver at a time. As each receiver executes in turn, it can propagate a result to the next receiver, or it can completely abort the broadcast so that it won't be passed to other receivers. The order receivers run in can be controlled with the android:priority attribute of the matching intent-filter; receivers with the same priority will be run in an arbitrary order.

method sends broadcasts to one receiver at a time. As each receiver executes in turn, it can propagate a result to the next receiver, or it can completely abort the broadcast so that it won't be passed to other receivers. The order receivers run in can be controlled with the android:priority attribute of the matching intent-filter; receivers with the same priority will be run in an arbitrary order. The sendBroadcast(Intent) method sends broadcasts to all receivers in an undefined order. This is called a Normal Broadcast. This is more efficient, but means that receivers cannot read results from other receivers, propagate data received from the broadcast, or abort the broadcast.

method sends broadcasts to all receivers in an undefined order. This is called a Normal Broadcast. This is more efficient, but means that receivers cannot read results from other receivers, propagate data received from the broadcast, or abort the broadcast. The LocalBroadcastManager.sendBroadcast method sends broadcasts to receivers that are in the same app as the sender. If you don't need to send broadcasts across apps, use local broadcasts. The implementation is much more efficient (no interprocess communication needed) and you don't need to worry about any security issues related to other apps being able to receive or send your broadcasts.

The following code snippet demonstrates how to send a broadcast by creating an Intent and calling sendBroadcast(Intent) .

Kotlin Intent().also { intent -> intent.setAction("com.example.broadcast.MY_NOTIFICATION") intent.putExtra("data", "Notice me senpai!") sendBroadcast(intent) } Java Intent intent = new Intent(); intent.setAction("com.example.broadcast.MY_NOTIFICATION"); intent.putExtra("data","Notice me senpai!"); sendBroadcast(intent);

The broadcast message is wrapped in an Intent object. The intent's action string must provide the app's Java package name syntax and uniquely identify the broadcast event. You can attach additional information to the intent with putExtra(String, Bundle) . You can also limit a broadcast to a set of apps in the same organization by calling setPackage(String) on the intent.

Note: startActivity(Intent) , these actions are completely unrelated. Broadcast receivers can't see or capture intents used to start an activity; likewise, when you broadcast an intent, you can't find or start an activity. Although intents are used for both sending broadcasts and starting activities with, these actions are completely unrelated. Broadcast receivers can't see or capture intents used to start an activity; likewise, when you broadcast an intent, you can't find or start an activity.

Restricting broadcasts with permissions

Permissions allow you to restrict broadcasts to the set of apps that hold certain permissions. You can enforce restrictions on either the sender or receiver of a broadcast.

Sending with permissions

When you call sendBroadcast(Intent, String) or sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) , you can specify a permission parameter. Only receivers who have requested that permission with the tag in their manifest (and subsequently been granted the permission if it is dangerous) can receive the broadcast. For example, the following code sends a broadcast:

Kotlin sendBroadcast(Intent("com.example.NOTIFY"), Manifest.permission.SEND_SMS) Java sendBroadcast(new Intent("com.example.NOTIFY"), Manifest.permission.SEND_SMS);

To receive the broadcast, the receiving app must request the permission as shown below:

<uses-permission android:name="android.permission.SEND_SMS"/>

You can specify either an existing system permission like SEND_SMS or define a custom permission with the <permission> element. For information on permissions and security in general, see the System Permissions.

Note: Custom permissions are registered when the app is installed. The app that defines the custom permission must be installed before the app that uses it.

Receiving with permissions

If you specify a permission parameter when registering a broadcast receiver (either with registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) or in <receiver> tag in your manifest), then only broadcasters who have requested the permission with the <uses-permission> tag in their manifest (and subsequently been granted the permission if it is dangerous) can send an Intent to the receiver.

For example, assume your receiving app has a manifest-declared receiver as shown below:

<receiver android:name=".MyBroadcastReceiver" android:permission="android.permission.SEND_SMS"> <intent-filter> <action android:name="android.intent.action.AIRPLANE_MODE"/> </intent-filter> </receiver>

Or your receiving app has a context-registered receiver as shown below:

Kotlin var filter = IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED) registerReceiver(receiver, filter, Manifest.permission.SEND_SMS, null ) Java IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); registerReceiver(receiver, filter, Manifest.permission.SEND_SMS, null );

Then, to be able to send broadcasts to those receivers, the sending app must request the permission as shown below:

<uses-permission android:name="android.permission.SEND_SMS"/>

Security considerations and best practices

Here are some security considerations and best practices for sending and receiving broadcasts: