Android Broadcast Receivers: A Beginner‘s Guide

As an Android developer, you‘ll often need your apps to respond to events originating from outside the app itself, such as changes to device connectivity, battery level, or timezone. Broadcast receivers provide a flexible mechanism for your app components to listen for and react to these system-wide events.

In this beginner‘s guide, we‘ll cover everything you need to know to start leveraging the power of broadcast receivers in your Android apps. You‘ll learn what broadcast receivers are, how to register them, how to create your own custom receiver classes, best practices to keep in mind, and alternatives to consider. Let‘s dive in!

What are Broadcast Receivers?

A broadcast receiver is a component that allows your app to receive intents that are broadcast by the Android system or other apps, even when your app isn‘t currently running. You can think of broadcasts as system-wide messages notifying apps of events they may be interested in.

There are two main types of broadcasts:

  • System broadcasts sent by the Android OS to notify apps of system events (e.g. device boot completed, battery low, etc.)
  • App-specific broadcasts sent by apps to notify other components of app-specific events.

Broadcast receivers act as mailboxes that the system or apps can drop messages into. Your app can register a receiver to listen for specific broadcast intents. When a matching intent is broadcast, the system automatically routes it to your app‘s registered receiver.

Registering Broadcast Receivers

There are two ways you can register a broadcast receiver in your app:

1. Declaring in AndroidManifest.xml

You can statically register a broadcast receiver by declaring it in your app manifest using the <receiver> tag:

<receiver
    android:name=".MyBroadcastReceiver"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
    </intent-filter>
</receiver>

The android:name attribute specifies the class name of your broadcast receiver. The android:exported attribute indicates whether the receiver can receive broadcasts from outside your app.

Inside the <receiver> tag, you specify <intent-filter> tags to define the broadcast actions your receiver subscribes to. The example above listens for the BOOT_COMPLETED system broadcast.

2. Registering Dynamically

You can also register and unregister receivers dynamically at runtime using the Context.registerReceiver() and unregisterReceiver() methods:

val receiver = MyBroadcastReceiver()

// Register receiver
val filter = IntentFilter().apply {
    addAction(ConnectivityManager.CONNECTIVITY_ACTION) 
    addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED)
}
registerReceiver(receiver, filter)

// Unregister when no longer needed
unregisterReceiver(receiver)

With this approach, your receiver will only receive broadcasts as long as it‘s registered. It‘s important to always unregister the receiver when it‘s no longer needed to avoid leaking memory.

Creating a Custom BroadcastReceiver

To create your own broadcast receiver, extend the BroadcastReceiver class and override its onReceive() method:

class MyBroadcastReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        // Handle received broadcast
        val action = intent.action
        Log.d("MyBroadcastReceiver", "Received action: $action")

        when (action) {
            ConnectivityManager.CONNECTIVITY_ACTION -> {
                val noConnectivity = intent.getBooleanExtra(
                    ConnectivityManager.EXTRA_NO_CONNECTIVITY, false
                )
                if (!noConnectivity) {
                    // Connectivity restored, react accordingly
                }
            }
            // Handle other actions...
        }
    }
}

The onReceive() method is called by the system when an intent matching one of your registered intent filters is broadcast. This is where you define how your app reacts to the received broadcast.

The method provides access to a Context object representing the application context, and an Intent object containing information about the received broadcast.

It‘s important to keep onReceive() implementations short and fast, as the method executes on your app‘s main thread. If you need to perform long running operations, start a background service instead.

Broadcasting Events in Your App

In addition to receiving system broadcasts, you can use broadcast receivers for communication between components within your own app.

To send a broadcast intent, you use one of the following Context methods:

  • sendOrderedBroadcast(): Delivers the intent to one registered receiver at a time, in priority order. Receivers can propagate results to subsequent receivers.

  • sendBroadcast(): Delivers the intent to all registered receivers simultaneously, with no priority and no mechanism for propagation or results.

  • LocalBroadcastManager.sendBroadcast(): Similar to sendBroadcast() but the broadcast stays within your app and isn‘t visible to other apps.

For example, to broadcast a custom action indicating that a file has finished downloading in your app:

val intent = Intent("com.example.FILE_DOWNLOADED")
intent.putExtra("fileUrl", downloadedFileUrl)
sendBroadcast(intent)

Components in your app that have registered to receive the com.example.FILE_DOWNLOADED action will have their onReceive() method triggered, passing along the downloaded file URL in the intent extras.

Gotchas and Limitations

When working with broadcast receivers, there are some gotchas and limitations to keep in mind, especially in more recent versions of Android.

Firstly, avoid broadcasting sensitive data using implicit intents, as any app with a matching receiver will be able to access that data. Use permissions to limit access or consider explicit intents instead.

Secondly, think twice before starting an activity directly from a broadcast receiver. The resulting user experience is often jarring. Instead, consider showing a notification that then launches the relevant activity when tapped.

Android has introduced incremental limitations on broadcast receivers over the years to improve performance and battery life:

  • Android 7.0: Disables certain system broadcasts like ACTION_NEW_PICTURE and ACTION_NEW_VIDEO for manifest-declared receivers.

  • Android 8.0: Requires most implicit broadcasts to be registered dynamically, with only a whitelisted set allowed in the manifest.

  • Android 9.0: Provides less information in certain Wi-Fi and network state broadcasts for privacy reasons.

Be sure to check the documentation for the Android version you‘re targeting to understand what restrictions may apply.

Alternatives to Broadcast Receivers

While broadcast receivers are a powerful tool, they may not always be the best solution. Here are a couple alternatives to consider:

  • LocalBroadcastManager: If your broadcasts are internal to your app, use LocalBroadcastManager to avoid system overhead and unintended interactions with other apps.

  • JobScheduler: For deferrable background tasks that don‘t need to run immediately, JobScheduler provides a battery-efficient API to schedule jobs based on criteria like network availability or charging state.

Example Apps

To see working examples of the concepts covered in this guide, check out the following sample projects:

  • Custom Broadcast – Demonstrates registering a receiver in the manifest to listen for a custom action.

  • Dynamic Broadcast – Shows how to dynamically register and unregister a connectivity change receiver.

  • LocalBroadcastManager – Illustrates communicating between components in an app using LocalBroadcastManager.

Conclusion

We‘ve covered a lot of ground in this guide to Android broadcast receivers. You should now have a solid understanding of what broadcast receivers are, how to register them, how to send your own broadcasts, and best practices to adhere to.

Broadcast receivers are a fundamental building block for creating Android apps that are well integrated with the surrounding system and other apps. By responding to system events and app-specific broadcasts, you can craft rich, dynamic experiences for your users.

That said, it‘s also important to understand the limitations and performance implications of broadcast receivers, especially in light of Android‘s evolving restrictions in recent versions. In many cases, newer APIs like JobScheduler may be a better choice for scheduling background work.

I hope this guide has been a helpful introduction to the world of Android broadcast receivers. Remember to consult the official Android documentation for the most up-to-date and detailed information on changes between versions. Happy broadcasting!

Similar Posts