Skip to content

Android Broadcast Receivers Explained - Complete Developer Guide

DodaTech Updated 2026-06-29 2 min read

In this tutorial, you'll learn how Android Broadcast Receivers work for system-wide and app-internal event communication with practical Kotlin examples.

What You'll Learn

how Android Broadcast Receivers work for system-wide and app-internal event communication with practical Kotlin examples — Broadcast Receivers allow your app to respond to system events (airplane mode, battery low, boot completed) and communicate between components without tight coupling.

Why It Matters

Broadcast Receivers allow your app to respond to system events (airplane mode, battery low, boot completed) and communicate between components without tight coupling.

Real-World Use

A file manager app listens for ACTION_MEDIA_MOUNTED to refresh the file list, uses a custom broadcast for bookmarks synced from the cloud, and registers a boot receiver to start cleanup jobs.

Learning Path

flowchart LR
    [Services] --> [Broadcast Receivers] --> [WorkManager] --> [Notifications]
    style 2 fill:#4CAF50,color:#fff

Dynamic Registration

class MainActivity : AppCompatActivity() {
    private val airplaneModeReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            val isEnabled = intent.getBooleanExtra("state", false)
            Toast.makeText(context, "Airplane mode: ${if (isEnabled) "ON" else "OFF"}", Toast.LENGTH_SHORT).show()
        }
    }
    override fun onStart() {
        super.onStart()
        registerReceiver(airplaneModeReceiver, IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED))
    }
    override fun onStop() {
        super.onStop()
        unregisterReceiver(airplaneModeReceiver)
    }
}

Expected output: The receiver is registered in onStart and unregistered in onStop, preventing leaks.

Static Registration for Boot Completed

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<receiver android:name=".BootReceiver" android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

Expected output: The receiver is declared in the manifest and triggers on device boot.

Custom Broadcast Sending

val intent = Intent("com.dodatech.ACTION_UPDATE_FINISHED").apply {
    putExtra("result", "success")
    putExtra("items_updated", 42)
}
sendBroadcast(intent, "com.dodatech.READ_RESULTS")

Expected output: A custom broadcast is sent with a permission restriction, ensuring only receivers with the matching permission can receive it.

Common Errors

  1. Leaked receiver - always unregister in onStop or onPause; Android 14+ throws exception for leaks
  2. BroadcastReceiver running on main thread - use goAsync() for longer work
  3. Static receivers not working on Android 8+ - most implicit broadcasts blocked for manifest-declared receivers
  4. SecurityException for protected broadcasts - some system broadcasts require permissions to send
  5. Mutable intent broadcasts expose data to other apps - use permissions or in-app communication channels

Practice Questions

  1. What is the difference between normal and ordered broadcasts?

  2. Why are most manifest-declared receivers ineffective on Android 8+?

  3. When should you use goAsync() in a BroadcastReceiver?

  4. How do you protect a custom broadcast from other apps?

  5. What replaced LocalBroadcastManager for in-app communication?

Challenge

Build a battery monitor: dynamically register for ACTION_BATTERY_CHANGED and ACTION_BATTERY_LOW. Show battery percentage and display a notification when low. Unregister correctly.

Real-World Task

Create a sync-trigger system: when a settings change broadcast is sent (custom intent), a BroadcastReceiver intercepts it and triggers a WorkManager sync worker. Use ordered broadcasts for logging.

Frequently Asked Questions

{{< faq question="Can a BroadcastReceiver start an activity?">}} Yes, call startActivity() with FLAG_ACTIVITY_NEW_TASK since the receiver is not in an activity context. {{< /faq >}}

{{< faq question="How long can a receiver run before the system kills it?">}} About 10 seconds. Use goAsync() for up to 10 additional seconds on a background thread. {{< /faq >}}

{{< faq question="Can I register one receiver for multiple filters?">}} Yes, register multiple IntentFilters or handle multiple actions by checking intent.action in onReceive. {{< /faq >}}

Security Tip: When sending sensitive data via broadcasts, include a signature-level permission to restrict receivers to your own app. Use sendBroadcast(intent, permission) with protectionLevel='signature'.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro