Android
BroadcastReceiver
multithreading
application development
Java

Are Android's BroadcastReceivers started in a new thread?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

No, a normal Android BroadcastReceiver does not start on a fresh worker thread by default. Its onReceive() callback usually runs on the main thread of your app process. That is why receiver code must return quickly and should hand off real work to another component instead of blocking in place.

What Thread onReceive() Runs On

The default mental model is simple: a broadcast arrives, Android enters your process, and calls onReceive() on the main thread.

java
1public class PowerReceiver extends BroadcastReceiver {
2    @Override
3    public void onReceive(Context context, Intent intent) {
4        Log.d("Receiver", "Thread = " + Thread.currentThread().getName());
5    }
6}

If you log the thread name, you will typically see the app's main thread rather than a dedicated background worker.

This matters because the main thread is also responsible for UI responsiveness and other app event handling.

Why the Main-Thread Behavior Is Important

A receiver is not designed to be a long-running job processor. Its job is to react quickly to an event and then return.

Bad pattern:

java
1@Override
2public void onReceive(Context context, Intent intent) {
3    performLargeNetworkCall();
4}

That can stall the main thread, make the app unresponsive, and cause the system to treat the receiver as misbehaving.

The safer pattern is:

  1. inspect the broadcast
  2. decide what should happen
  3. schedule or delegate the actual work
  4. return quickly

Use goAsync() for Short Deferred Work

If you need a little more time after onReceive() returns, Android provides goAsync().

java
1@Override
2public void onReceive(Context context, Intent intent) {
3    PendingResult result = goAsync();
4
5    new Thread(() -> {
6        try {
7            doBackgroundWork();
8        } finally {
9            result.finish();
10        }
11    }).start();
12}

This is useful for short asynchronous continuation. It is not a replacement for a proper background job scheduler. You must call finish() when the work is done.

Prefer WorkManager for Real Background Jobs

If a broadcast should trigger actual processing, use WorkManager instead of doing the whole job inside the receiver.

java
1public class SyncReceiver extends BroadcastReceiver {
2    @Override
3    public void onReceive(Context context, Intent intent) {
4        OneTimeWorkRequest request =
5            new OneTimeWorkRequest.Builder(SyncWorker.class).build();
6
7        WorkManager.getInstance(context).enqueue(request);
8    }
9}

This is usually the right production design because WorkManager handles retries, constraints, process death, and scheduling more safely than a raw thread.

Ordered Broadcasts Do Not Change the Core Rule

Even with ordered broadcasts, the default callback is still not a magic worker-thread execution model. Ordered broadcasts change delivery semantics, not the basic rule that onReceive() should stay short and non-blocking.

Think of the receiver as an event entry point, not as a service.

Dynamic and Manifest Registration

Whether the receiver is registered in the manifest or registered dynamically in code, the same design guidance still applies: do not assume heavy work belongs in onReceive().

If the process is not running, Android may start it to deliver the broadcast. That does not mean the receiver suddenly owns a special background execution context. It simply means the process is active so the callback can run.

Common Pitfalls

  • Assuming a BroadcastReceiver automatically runs on a worker thread.
  • Doing network, disk, or database work directly inside onReceive().
  • Using goAsync() and forgetting to call finish().
  • Spawning ad hoc threads for durable work that belongs in WorkManager.
  • Treating receiver code as if it were a long-lived background service.

Summary

  • A normal Android BroadcastReceiver is not started on a new thread by default.
  • 'onReceive() usually executes on the main thread.'
  • Receiver logic should be quick and non-blocking.
  • Use goAsync() only for short follow-up work.
  • For real background processing, delegate to WorkManager or another structured background API.

Course illustration
Course illustration

All Rights Reserved.