Android Broadcast Receivers: The Complete Guide to Listening and Sending Events (Part 5)
Have you ever wondered how your favorite Android app instantly knows when your phone enters airplane mode or when the battery drops below 15%? The secret lies in a core Android component: the Broadcast Receiver.
Whether you are looking to understand the fundamentals of Broadcast Receivers (BR) or want to learn how to implement and trigger them in your application, this guide has you covered. Let’s dive into how these invisible messengers keep your apps connected to the Android system and each other.
What is a Broadcast Receiver?
Within the Android operating system, certain background events are constantly happening. Examples include the user turning Airplane Mode on or off, switching Wi-Fi or Bluetooth states, or simply connecting their phone to a charger.
Whenever these actions occur, the Android OS broadcasts these changes across the entire system as broadcast events. Applications can capture these events to stay updated. By setting up a Broadcast Receiver inside your app, you can keep a close eye on specific events happening in the Android OS or on the device itself.
Types of Broadcast Events
There are two main types of broadcast events your receiver can listen for:
- System Events: These are events inherently associated with the Android system itself, such as turning on Airplane Mode, toggling Wi-Fi, or connecting to a charger.
- Custom Events: These are non-system events created by third-party applications or your own app. Your application can create custom events to be broadcasted and sent to other apps across the Android operating system.
Two Methods for Implementing Broadcast Receivers
To implement a broadcast receiver in your application, you can use two primary approaches:
- The Static Method: You declare your broadcast receiver directly in the
AndroidManifest.xmlfile. Because it is static, you cannot change this receiver dynamically at runtime. - The Dynamic Method: You programmatically create and register the receiver during the app’s runtime. This gives you the flexibility to turn the broadcast receiver on or off while the application is running.
Let’s dive into three practical examples to see how both methods work.
Example 1: Creating a Custom Static Broadcast Receiver
In this first example, we will create a static receiver designed to listen for our own custom events.
Step 1: Create the Kotlin Class
First, create a new Kotlin class file (e.g., StaticReceiver.kt) and add the BroadcastReceiver constructor to turn the class into a receiver. Next, override the onReceive event. This function triggers whenever your app receives a broadcast.
Broadcasts are received via an Intent. Think of an intent as a messenger that transfers data from one application to another. Inside the onReceive function, we use this intent to extract extra data (using a key like extra_msg). Once the broadcast is received, you can display this extracted data as a simple Toast message or print it in the Android Logcat.
// StaticReceiver.kt
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.widget.Toast
class StaticReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// Extract the message from the intent
val message = intent.getStringExtra("extra_msg") ?: "No message"
// Display the message as a Toast
Toast.makeText(context, message, Toast.LENGTH_LONG).show()
}
}
Step 2: Register in the Android Manifest
Next, you must mention this receiver in your AndroidManifest.xml file:
- Open your manifest and add a
<receiver>tag. - Add the name of your Kotlin file (e.g.,
.StaticReceiver). - Set the
android:exportedproperty. Set it totrueif you want to allow third-party applications to trigger your receiver. If you only want to receive broadcasts from your own app, set it tofalse. - Inside the receiver tag, create an
<intent-filter>and add an action name (e.g.,com.devish.broadcastreceiverapp.static_action). This unique string links directly to your custom broadcast event.
<!-- AndroidManifest.xml -->
<receiver
android:name=".StaticReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.devish.broadcastreceiverapp.static_action" />
</intent-filter>
</receiver>
Step 3: Trigger the Broadcast
To trigger this static receiver, go to your MainActivity.kt file. You can attach this logic to a button click:
- Create a new Intent and pass in the exact custom action string you defined in the manifest.
- Use
putExtrato add your message data (e.g., “Hello static event trigger”). - Mention your package name using
setPackage(). - Finally, use the context to call
sendBroadcast(intent).
// MainActivity.kt
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Button(onClick = { triggerStaticBroadcast() }) {
Text("Send Static Broadcast")
}
}
}
private fun triggerStaticBroadcast() {
val intent = Intent("com.devish.broadcastreceiverapp.static_action")
intent.putExtra("extra_msg", "Hello static event trigger")
// Restrict the broadcast to your own app package
intent.setPackage(packageName)
sendBroadcast(intent)
}
}
When the button is clicked, the system routes the event to your StaticReceiver class, which captures it and displays your Toast message!
Example 2: Listening for System Events (Airplane Mode)
Now, let’s look at how to listen for a System Event, specifically toggling Airplane Mode.
Step 1: Set Up the Receiver
Create a new file named AeroplaneModeReceiver.kt. Just like the first example, use the intent inside the onReceive block to check the state. In this case, the state is a boolean—if it returns true, Airplane Mode is on; if false, it is off. We will display this state as a Toast message.
// AeroplaneModeReceiver.kt
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.widget.Toast
class AeroplaneModeReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// The Android OS passes the state as a boolean using the key "state"
val isAirplaneModeOn = intent.getBooleanExtra("state", false)
if (isAirplaneModeOn) {
Toast.makeText(context, "Airplane Mode is ON", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(context, "Airplane Mode is OFF", Toast.LENGTH_LONG).show()
}
}
}
Step 2: Declare the System Action
In your AndroidManifest.xml, create a <receiver> tag for this file and set exported="true". The most critical step here is adding the specific action string reserved by the Android OS for this event: android.intent.action.AIRPLANE_MODE.
(Note: If you want to listen for charging or Bluetooth events, you would use their respective reserved system strings, which can be found in the official Android documentation).
<!-- AndroidManifest.xml -->
<receiver
android:name=".AeroplaneModeReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.AIRPLANE_MODE" />
</intent-filter>
</receiver>
Step 3: Register Dynamically at Runtime
Since we are listening to system events, we need to register this receiver dynamically at runtime. Using Jetpack Compose in the MainActivity.kt file:
- Inside a
DisposableEffectfunction (which runs once at the start), create anIntentFilterthat targets theAIRPLANE_MODEaction string. - Call
registerReceiver(), passing in yourAeroplaneModeReceiverand the intent filter. - Crucial Step: When the user leaves the screen or application, you must clean up. Go to the
onDisposefunction and callunregisterReceiver()to safely detach it.
// MainActivity.kt
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.platform.LocalContext
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AirplaneModeScreen()
}
}
}
@Composable
fun AirplaneModeScreen() {
val context = LocalContext.current
val airplaneReceiver = AeroplaneModeReceiver()
DisposableEffect(Unit) {
// Create the intent filter for the system event
val filter = IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)
// Register the receiver dynamically
context.registerReceiver(airplaneReceiver, filter)
// Clean up when the Composable is removed from the screen
onDispose {
context.unregisterReceiver(airplaneReceiver)
}
}
// Your UI goes here...
}
When you run this on a virtual device and toggle Airplane Mode, your app will immediately display “Airplane Mode is ON” or “Airplane Mode is OFF.”
Example 3: Creating a Dynamic Custom Broadcast Receiver
Unlike the static method, you do not need to touch the AndroidManifest.xml file for a purely dynamic custom receiver. Everything is handled during runtime.
Step 1 & 2: Create the Receiver Object and Register it
- Define a custom name variable for your broadcast action (e.g.,
custom_action). - Create an object of
BroadcastReceiverdirectly in your code. - Inside this object, override the
onReceivefunction to extract theextra_datastring from the intent and save it to a variable. - Navigate to the
onStartevent of your activity. Here, create anIntentFilterusing yourcustom_actionstring. The purpose of this filter is to ignore other system broadcasts and ensure you only receive your specific custom event. Next, register it by callingregisterReceiver().
Step 3 & 4: Send the Custom Broadcast and Unregister
Create a function to send the event. Generate an Intent with your custom_action string, attach your putExtra data (e.g., “Hello from dynamic receiver”), and execute sendBroadcast(intent).
To prevent memory leaks, always remember to call unregisterReceiver() when the user closes down the app or leaves the screen.
// MainActivity.kt
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
class MainActivity : ComponentActivity() {
// Define the custom action string
private val customAction = "com.devish.broadcastreceiverapp.custom_action"
// Variable to hold the received data
private var receivedMessage = ""
// Create the receiver object programmatically
private val dynamicReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// Extract data and save it
receivedMessage = intent.getStringExtra("extra_data") ?: "No data"
Toast.makeText(context, receivedMessage, Toast.LENGTH_SHORT).show()
}
}
override fun onStart() {
super.onStart()
// Step 2: Create the filter and register the receiver
val filter = IntentFilter(customAction)
registerReceiver(dynamicReceiver, filter)
}
override fun onStop() {
super.onStop()
// Step 4: Unregister to prevent memory leaks
unregisterReceiver(dynamicReceiver)
}
// Step 3: Function to send the custom broadcast
private fun sendDynamicBroadcast() {
val intent = Intent(customAction)
intent.putExtra("extra_data", "Hello from dynamic receiver")
sendBroadcast(intent)
}
}
Conclusion
Implementing Broadcast Receivers is straightforward once you understand how intents and intent filters work together. You can choose either the static method (via the Android Manifest) or the dynamic method (via programmatic runtime registration), depending on what works best for your specific application needs.
Thank you for reading! If you want to master more essential skills, be sure to check out our full playlist on the Fundamentals of Android App Development to learn everything you need to build high-quality Android applications.
📌 Full Course Playlist https://www.youtube.com/playlist?list=PLO1OrQEU0vHNmD9Xqzs-qXwzzwrDvdhVu
~ ~ THANK YOU FOR READING ~ ~