Skip to main content

Working with BroadcastReceivers

1. Creating BroadcastReceiver Class

Property Description Example
onReceive(Context, Intent) Callback method invoked when broadcast is received
override fun onReceive(context: Context, intent: Intent) {
    // Handle broadcast
}

Example: Basic BroadcastReceiver

class MyBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val action = intent.action
        if (action == Intent.ACTION_BOOT_COMPLETED) {
            // Handle boot completed
        }
    }
}
Note: BroadcastReceiver must be declared in AndroidManifest.xml or registered dynamically.

2. Registering Receivers in Manifest

Property Description Values
receiver Declares a broadcast receiver <receiver>
android:name Full class name of the receiver com.example.MyReceiver
android:enabled Enables or disables the receiver true | false
android:exported Allows other apps to interact with the receiver true | false
android:permission Permission required to send broadcasts to this receiver android.permission.SEND_SMS

Example: Declaring a BroadcastReceiver in AndroidManifest.xml

<receiver
    android:name=".MyBroadcastReceiver"
    android:enabled="true"
    android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>
Note: For android:exported, set to true only if the receiver should be accessible by other apps. Otherwise, set to false for internal use.

3. Registering Receivers Dynamically

Method Description Notes
registerReceiver() Registers a receiver dynamically at runtime Must be unregistered to prevent memory leaks
unregisterReceiver() Unregisters a receiver to prevent memory leaks Must be called in the same context where it was registered

Example: Registering a Dynamic Receiver

class MainActivity : AppCompatActivity() {
    private val receiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            // Handle broadcast
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val filter = IntentFilter("com.example.CUSTOM_ACTION")
        registerReceiver(receiver, filter)
    }

    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(receiver)
    }
}
Note: Dynamic registration is only valid while the component (e.g., Activity) is alive. Once the component is destroyed, the receiver must be unregistered.

4. Unregistering Receivers

Unregistering a BroadcastReceiver is crucial to prevent memory leaks and ensure proper resource management. Android requires explicit unregistration of receivers that were registered programmatically.

Method Description Notes
unregisterReceiver() Unregisters a previously registered BroadcastReceiver. Must be called on the same context used for registration.

Example: Unregistering a BroadcastReceiver

class MainActivity : AppCompatActivity() {
    private val receiver = MyBroadcastReceiver()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Register the receiver
        registerReceiver(receiver, IntentFilter("com.example.MY_ACTION"))
    }

    override fun onDestroy() {
        super.onDestroy()
        // Unregister the receiver to prevent memory leaks
        unregisterReceiver(receiver)
    }
}

class MyBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        // Handle broadcast
    }
}
Note: If a BroadcastReceiver is registered in the manifest, it does not need to be unregistered manually.
Warning: Failing to unregister a dynamically registered BroadcastReceiver can lead to memory leaks and unexpected behavior.

5. Sending Broadcasts

Method Description Notes
sendBroadcast() Sends a broadcast to all registered receivers Async, no result expected
sendOrderedBroadcast() Sends a broadcast to receivers in order Allows for result handling and aborting
sendStickyBroadcast() Sends a broadcast and keeps it in the system DEPRECATED since API 21

Example: Sending a Basic Broadcast

val intent = Intent("com.example.CUSTOM_ACTION")
intent.putExtra("message", "Hello from BroadcastReceiver")
sendBroadcast(intent)

Example: Sending Ordered Broadcast

val intent = Intent("com.example.ORDERED_ACTION")
intent.putExtra("data", "ordered data")
sendOrderedBroadcast(intent, null) { _, _ ->
    // Handle result if needed
}
Note: For security, use LocalBroadcastManager or Context.registerReceiver() for local broadcasts.

6. Handling System Broadcasts

Broadcast Permission Description
android.intent.action.BATTERY_CHANGED android.permission.BATTERY_STATS Provides information about the battery status, including level, health, and plugged state.
android.intent.action.BATTERY_LOW android.permission.BATTERY_STATS Indicates that the battery is low and the device should enter power-saving mode.
android.intent.action.BATTERY_OKAY android.permission.BATTERY_STATS Indicates that the battery is now at a normal level after being low.
android.intent.action.BOOT_COMPLETED android.permission.RECEIVE_BOOT_COMPLETED Indicates that the system has completed booting and is ready to receive broadcasts.
android.intent.action.SCREEN_ON android.permission.RECEIVE_BOOT_COMPLETED Indicates that the screen has turned on.
android.intent.action.SCREEN_OFF android.permission.RECEIVE_BOOT_COMPLETED Indicates that the screen has turned off.

Example: Receiving Battery Status Changes

class BatteryReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (Intent.ACTION_BATTERY_CHANGED == intent.action) {
            val level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
            val scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
            val batteryPct = (level / scale) * 100
            Log.d("Battery", "Battery Level: $batteryPct%")
        }
    }
}

Example: Registering for Boot Completion

class BootReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (Intent.ACTION_BOOT_COMPLETED == intent.action) {
            // Start background service or perform initialization tasks
            val serviceIntent = Intent(context, MyBackgroundService::class.java)
            context.startService(serviceIntent)
        }
    }
}
Note: To receive system broadcasts like BATTERY_CHANGED, you must declare the appropriate permissions in your manifest and ensure that the receiver is registered either in code or in the manifest.

7. Setting Broadcast Permissions

Permission Description Required
android.permission.RECEIVE_BOOT_COMPLETED Allows application to receive the BOOT_COMPLETED system broadcast Yes, for boot-related broadcasts
android.permission.ACCESS_COARSE_LOCATION Allows application to access approximate location Yes, for location-related broadcasts
android.permission.ACCESS_FINE_LOCATION Allows application to access precise location Yes, for location-related broadcasts
android.permission.BLUETOOTH_ADMIN Allows applications to discover and pair Bluetooth devices Yes, for Bluetooth-related broadcasts
android.permission.INTERNET Allows application to open network sockets Yes, for network-related broadcasts
android.permission.READ_PHONE_STATE Allows read only access to phone state Yes, for phone state change broadcasts

Example: Declaring Permissions in Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <application>
        ...
    </application>
</manifest>
Note: Permissions must be declared in the AndroidManifest.xml file for broadcast receivers to function properly.

8. Creating Custom Broadcasts

Property Description Example
action Unique string identifier for the broadcast "com.example.CUSTOM_ACTION"
extras Bundle of data to send with the broadcast Intent.putExtra("message", "Hello")

Example: Sending a Custom Broadcast

val intent = Intent("com.example.CUSTOM_ACTION")
intent.putExtra("message", "Hello from BroadcastReceiver!")
sendBroadcast(intent)
Note: Custom broadcasts are sent using sendBroadcast() and can be received by any registered receiver in the app.

9. Using Implicit vs Explicit Broadcasts

Aspect Implicit Broadcast Explicit Broadcast
Intent Intent(intentAction) Intent(context, ReceiverClass::class.java)
Registration Declared in AndroidManifest.xml Registered programmatically using registerReceiver()
Scope Global to the app or system Targeted to specific receiver(s)
Security Less secure; can be intercepted More secure; only known receivers receive
Performance Higher overhead due to system-wide dispatch Better performance; direct delivery

Example: Sending an Implicit Broadcast

val intent = Intent("com.example.CUSTOM_ACTION")
intent.putExtra("data", "someValue")
sendBroadcast(intent)

Example: Sending an Explicit Broadcast

val intent = Intent(this, MyReceiver::class.java)
intent.putExtra("data", "someValue")
sendBroadcast(intent)
Note: As of Android 8.0 (API level 26), implicit broadcasts for system events are restricted to improve battery life and security.

10. Handling Broadcast Exceptions

Exception Type Description Handling Strategy
SecurityException Thrown when the app does not have permission to receive a broadcast. Ensure proper permissions are declared in AndroidManifest.xml.
IllegalStateException Occurs when trying to register a receiver after the component has been destroyed. Register receivers in onResume() and unregister in onPause().
RuntimeException Generic exception for runtime issues in broadcast handling. Wrap receiver logic in try-catch blocks to handle gracefully.

Example: Exception Handling in BroadcastReceiver

class MyBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        try {
            // Handle broadcast logic here
            val data = intent?.getStringExtra("data")
            if (data == null) {
                throw IllegalArgumentException("Data is null")
            }
        } catch (e: SecurityException) {
            Log.e("BroadcastReceiver", "Security error: ${e.message}")
        } catch (e: IllegalStateException) {
            Log.e("BroadcastReceiver", "Illegal state error: ${e.message}")
        } catch (e: RuntimeException) {
            Log.e("BroadcastReceiver", "Runtime error: ${e.message}")
        }
    }
}
Note: Always log exceptions for debugging and avoid letting unhandled exceptions crash the app.

Was this article helpful?