Broadcast Receivers
A BroadcastReceiver is a component that listens for and reacts to broadcast messages – one-way announcements that something happened. Broadcasts can come from the system (battery low, connectivity changed, device booted) or from your own app (a recipe was just added, a sync finished).
Unlike a Service, a BroadcastReceiver doesn't keep running – it wakes up briefly to handle one event and then it's done. That makes it a good fit for RecipeBox to react to things like "the battery is low, so pause any background photo uploads" without keeping a whole component alive just to wait for that moment.
Extending BroadcastReceiver
Every receiver overrides a single method, onReceive(), which is called when a matching broadcast arrives:
// BatteryLowReceiver.java
public class BatteryLowReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BATTERY_LOW.equals(intent.getAction())) {
// Pause background recipe-photo uploads to save power
RecipePhotoUploadQueue.getInstance(context).pauseAll();
Log.d("RecipeBox", "Battery low – paused photo uploads");
}
}
}
- onReceive() runs on the main thread and must return quickly – no long-running work here.
- Checking intent.getAction() lets one receiver handle multiple broadcast types if needed.
- Intent.ACTION_BATTERY_LOW is a system broadcast the OS sends when the battery is critically low.
Static vs. Dynamic Registration
A receiver only fires once it's registered somewhere. There are two ways to do that:
| Registration | Where declared | Lifetime | Best for |
|---|---|---|---|
| Static (manifest) | <receiver> tag in AndroidManifest.xml | Can be triggered even if the app process isn't running | A shrinking list of broadcasts still allowed to wake up an app (e.g. BOOT_COMPLETED) |
| Dynamic (code) | registerReceiver() in an Activity/Service/Application | Only active while that component is alive; must call unregisterReceiver() to stop it | Most system broadcasts today, plus any broadcast you only care about while the UI is visible |
// Registering dynamically, e.g. in onStart()
private final BatteryLowReceiver batteryReceiver = new BatteryLowReceiver();
@Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_LOW);
registerReceiver(batteryReceiver, filter);
}
@Override
protected void onStop() {
super.onStop();
unregisterReceiver(batteryReceiver);
}
Custom Broadcasts Within Your Own App
You can also broadcast events that only your own app cares about – for example, telling every screen in RecipeBox that "a new recipe was just added" so a list can refresh itself.
// Sending a custom broadcast after saving a recipe
public static final String ACTION_RECIPE_ADDED = "com.recipebox.RECIPE_ADDED";
public void onRecipeSaved(Recipe recipe) {
Intent intent = new Intent(ACTION_RECIPE_ADDED);
intent.putExtra("recipeId", recipe.getId());
context.sendBroadcast(intent);
}
// A receiver elsewhere in the app listening for it
public class RecipeAddedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
long recipeId = intent.getLongExtra("recipeId", -1);
// e.g. refresh the RecyclerView showing the recipe list
recipeListViewModel.reload();
}
}
Declaring a Static Receiver (When You Need One)
For the few broadcasts still allowed to wake an app via the manifest, declare the receiver like this:
<receiver
android:name=".BootReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Troubleshooting
Receiver never fires? → For most system broadcasts, confirm it's registered dynamically, not just declared in the manifest – newer Android versions ignore manifest-only registrations for implicit broadcasts.
App crashes with "Receiver not registered"? → You called unregisterReceiver() twice, or the Activity was destroyed before you unregistered; always pair registration with the matching lifecycle callback.
Custom broadcast not received by another screen? → Make sure the action string matches exactly, and that the listening receiver is registered before the broadcast is sent.