Understanding Android Broadcast Receivers
Learn in depth how does the Broadcast Receiver works in the Android Framework.

Senior Android Engineer from Bangladesh. Love to contribute in Open-Source. Indie Music Producer.
Search for a command to run...
Learn in depth how does the Broadcast Receiver works in the Android Framework.

Senior Android Engineer from Bangladesh. Love to contribute in Open-Source. Indie Music Producer.
This article is a great resource for anyone looking to understand Android Broadcast Receivers. The detailed breakdown of how they work, along with code snippets and use cases, makes it very practical. Excellent work!
Thank you!
In this series, I will write about Android App Development.
Efficiently manage Android component lifecycles with AndroidX Lifecycle Library, using key concepts and practical examples for cleaner architecture.
A practical guide to choosing the right coroutine primitive for your Android/Kotlin projects

This guide explains what Channels are, when to use them, how to use them correctly, and when NOT to use them β in simple, professional language. 1. What problem do Channels solve? In Kotlin coroutines, you often have multiple coroutines running at t...

Learn how to build preview-safe ViewModels in Jetpack Compose using Hilt.

HandlerInterceptor vs Filter in Spring Boot: A Practical Guide with JNDI Injection Prevention

Simple In-Memory Caching: A Tiny Trick with Massive Impact

A Broadcast Receiver in Android is a component that lets your app listen for system-wide or app-specific messages. These messages, called "intents," indicate that an event has happened. For example, your app can listen for system messages like the device booting up or a low battery warning, as well as custom messages from other apps.
When an event happens, the Android system creates a broadcast intent that multiple apps can receive. The broadcast can be:
System Broadcasts: Sent by the Android system (e.g., ACTION_BOOT_COMPLETED, ACTION_BATTERY_LOW).
Custom Broadcasts: Sent by your app or other apps.
The Broadcast Receiver acts as a link between the system or other apps and your app, letting it respond to events without always running in the background.
The Broadcast Receiver mechanism in Android uses the Binder IPC framework to send and receive messages between processes.
IPC stands for inter-process communication. It describes how different Android components talk to each other.
Intentsare messages that components can send and receive. They are used to pass data between processes. With intents, you can start services or activities, invoke broadcast receivers, and more.
Bundlesare data containers passed through intents. They are like serialized objects but faster on Android. You can read a bundle from an intent using thegetExtras()method.
Bindersallow activities and services to get a reference to another service. This lets you not only send messages to services but also call methods on them directly.
Broadcast Intent Creation:
- When a broadcast is sent, an
Intentobject holds the action and any additional data. TheIntentspecifies what kind of broadcast is being sent and may include extra information as key-value pairs.Broadcast Queue:
- The intent is placed into a queue managed by the system's
ActivityManagerService(AMS). The AMS is responsible for handling and managing the lifecycle of activities, services, and broadcasts.Identifying Receivers:
- The system identifies all registered receivers that match the intentβs action and other filters (e.g., category, data scheme). These receivers can be either statically registered in the AndroidManifest.xml file or dynamically registered at runtime using
Context.registerReceiver().Dispatching:
- The
onReceive()method of each matching receiver is called. For receivers registered in the application's manifest, theonReceive()method runs on the main thread of the application. For dynamically registered receivers, theonReceive()method runs on the main thread of the process where the receiver was registered.

Sending a Broadcast:
The app or system uses
Context.sendBroadcast()orContext.sendOrderedBroadcast()to send a broadcast intent.val intent = Intent("com.example.SAMPLE_ACTION") sendBroadcast(intent)Queuing in AMS:
The
ActivityManagerService(AMS) receives the broadcast request and enqueues it for processing.// Pseudo-code public void broadcastIntent(Intent intent) { BroadcastRecord r = new BroadcastRecord(intent); mBroadcastQueue.enqueue(r); }Finding Registered Receivers:
The system looks up all the receivers registered for the broadcast action.
// Pseudo-code List<BroadcastReceiver> matchingReceivers = findReceivers(intent);Delivering the Broadcast:
The intent is sent to the
onReceive()method of each matching receiver on the main thread.// Pseudo-code for (BroadcastReceiver receiver : matchingReceivers) { handler.post(() -> receiver.onReceive(context, intent)); }
// Pseudo-code for Broadcast Record
class BroadcastRecord {
Intent intent;
List<BroadcastReceiver> receivers;
BroadcastRecord(Intent intent) {
this.intent = intent;
this.receivers = new ArrayList<>();
}
}
// Pseudo-code for Broadcast Queue
class BroadcastQueue {
Queue<BroadcastRecord> queue = new LinkedList<>();
void enqueue(BroadcastRecord record) {
queue.add(record);
}
void processNext() {
BroadcastRecord record = queue.poll();
for (BroadcastReceiver receiver : record.receivers) {
handler.post(() -> receiver.onReceive(context, record.intent));
}
}
}
Threading and Performance:
handler.post() method ensures that the onReceive() method of the receiver runs on the main thread, which is crucial for updating the UI and interacting with other components safely.Ordered Broadcasts:
Priority and Permissions:
Broadcast Receivers can be registered in two ways:
Static Registration (in the Manifest):
Suitable for receiving broadcasts even when the app is not running.
Limited to system-defined broadcasts in Android 8.0 (Oreo) and above.
<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Dynamic Registration (in Code):
Useful for context-specific broadcasts.
The receiver is active only when the app is running.
val myReceiver = MyBroadcastReceiver()
val filter = IntentFilter(Intent.ACTION_BATTERY_LOW)
registerReceiver(myReceiver, filter)
How to send a broadcast:
val intent = Intent("com.example.SAMPLE_ACTION")
intent.putExtra("key", "value")
sendBroadcast(intent)
// or
val intent = Intent("com.example.SAMPLE_ACTION")
intent.putExtra("key", "value")
sendOrderedBroadcast(intent, null)
Implementing a Broadcast Receiver:
class MyBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
Intent.ACTION_BATTERY_LOW -> {
Toast.makeText(context, "Battery is low!", Toast.LENGTH_SHORT).show()
}
Intent.ACTION_BOOT_COMPLETED -> {
Toast.makeText(context, "Device just booted!", Toast.LENGTH_SHORT).show()
}
}
}
}
class ConnectivityReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork = connectivityManager.activeNetworkInfo
if (activeNetwork?.isConnected == true) {
// Network is connected
Toast.makeText(context, "Connected to the Internet", Toast.LENGTH_SHORT).show()
} else {
// Network is disconnected
Toast.makeText(context, "No Internet connection", Toast.LENGTH_SHORT).show()
}
}
}
// Register the receiver dynamically
val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
val receiver = ConnectivityReceiver()
registerReceiver(receiver, filter)
class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
// Start a service or schedule a job
Toast.makeText(context, "Device booted, starting service", Toast.LENGTH_SHORT).show()
// context.startService(Intent(context, MyService::class.java))
}
}
}
Broadcast Receivers can talk to other parts of the app using:
Intents: Send data to an activity or service.
LocalBroadcastManager: For communication within the app to avoid security risks of global broadcasts.
// Sending a local broadcast
val intent = Intent("com.example.SOME_ACTION")
intent.putExtra("data", "Hello from the receiver!")
LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
// Receiving the local broadcast
class LocalReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val data = intent.getStringExtra("data")
Toast.makeText(context, "Received data: $data", Toast.LENGTH_SHORT).show()
}
}
// Register the local receiver
val localReceiver = LocalReceiver()
val filter = IntentFilter("com.example.SOME_ACTION")
LocalBroadcastManager.getInstance(context).registerReceiver(localReceiver, filter)
- Limit Static Broadcast Receivers: To save battery, avoid static registration for broadcasts that happen often.
- Unregister Dynamic Receivers: Always unregister dynamic receivers in
onPause()oronDestroy()to avoid memory leaks.
- Use LocalBroadcastManager: For in-app broadcasts, use
LocalBroadcastManagerto improve security and performance.
Broadcast Receivers are a powerful feature in Android, allowing your app to respond to various system and app-specific events. By understanding how they work and following best practices, you can create responsive and efficient Android applications. Happy coding!
That's it for today. Happy Coding...