cswork / libraryOpen practice workspace

Articles

Android BroadcastReceiver Interview Questions: Export Rules and Lifecycle Boundaries

Prepare Android BroadcastReceiver interview questions on exported flags, registration lifetimes, goAsync deadlines, and durable background-work handoffs.

An Android BroadcastReceiver interview question can begin with a receiver that misses events after navigation, then pivot to one that accepts messages from another app. Fixing both requires three separate answers: who may send, how long the subscription exists, and how long the resulting work must survive. An export flag answers only the first question; goAsync() does not answer the third.

Use this guide to explain those boundaries, repair a lifecycle trace, and defend a background-work handoff. For a related architecture exercise, start with Design Android MVVM API Architecture and add a receiver whose owning screen disappears during a request.

Official Android documentation supports the platform rules cited below. The debugging scenarios and answer structures are original preparation exercises, not candidate reports or predictions of a particular employer's interview.

Android BroadcastReceiver interview questions: the quick answer

A BroadcastReceiver handles a broadcast Intent, typically through onReceive(). Manifest declarations and context registration make receivers available through different mechanisms. Receiver visibility, registration ownership and subsequent task execution must be evaluated independently. The receiver component reference describes the declaration mechanisms and permission boundary.

Before choosing code, ask for the device OS, target SDK, registration method, intended sender and required completion behavior. “A receiver should run in the background” is insufficient: a screen listener, a system-event entry point and a durable upload have different contracts.

DecisionQuestion to settleWhat does not settle it
Sender authorizationSame app, protected system action, or trusted external app?An unusual custom action name
Subscription lifetimeWhich owner needs events, and when does it unregister?Moving the registration to application context automatically
Execution lifetimeCan work finish promptly, or must it survive process loss?Starting a thread or calling goAsync()

For an interview explanation, name these contracts before describing the implementation. That gives the interviewer a precise assumption to change rather than a list of API names.

Which export rule applies to which receiver?

For manifest components with intent filters, apps targeting Android 12 or higher must explicitly declare android:exported; omission can prevent installation on Android 12 or higher. This is the Android 12 target behavior, not a rule about every runtime registration.

For context-registered receivers, the Android 14 target rules require apps targeting API 34 or higher to specify export behavior, with an exception for registrations receiving only system broadcasts through the platform Context.registerReceiver methods. Do not erase that exception by saying “every receiver always needs the same flag.”

The broadcasts overview distinguishes app-only RECEIVER_NOT_EXPORTED registration from external delivery. Some system broadcasts originate from privileged apps such as Bluetooth or telephony with different UIDs; an unexported dynamic receiver cannot receive all such broadcasts. Partition filters when different actions need different exposure.

Consider a custom example.SYNC_HINT action sent only by your own app. A screen-scoped context receiver with ContextCompat.RECEIVER_NOT_EXPORTED fits that stated sender boundary. If another app must send the hint, changing the flag is only the start of the design: define how the other app is trusted and which payload it may supply.

Never copy a target-SDK migration fix into every receiver without classifying its actions. A mixed filter can accidentally broaden a private command's exposure when you adjust it to receive a privileged system event.

Is an explicit intent or action filter an authorization check?

An action string expresses routing intent; it is not a secret. Setting a package narrows the destination, but does not prove the sender is authorized. The insecure-receiver security guidance recommends reducing exposure and protecting dynamic receivers through appropriate protected broadcasts or signature-level permissions.

For a same-signature companion application, a permission can be part of the intended trust contract. For an arbitrary third-party integration, “signature permission” is not a universal fix because unrelated applications do not share your signing identity. Clarify the integration rather than selecting a permission name that silently excludes legitimate senders.

An original security exercise: an exported receiver accepts an account ID and starts a synchronization request. A hostile sender supplies another user's ID. Rejecting malformed integers does not establish account authorization. The worker should resolve the authenticated account from trusted application state and revalidate whether the requested operation is allowed.

The repair therefore has two layers: restrict who can invoke the entry point, and restrict what that entry point can cause. Treat externally supplied URIs, paths and operation names as untrusted input. A receiver flag cannot replace the business authorization performed by the repository or server.

In your answer, identify the smallest valid message. A “refresh available” hint usually needs less authority than a command containing arbitrary destinations and credentials.

What owns a context-registered receiver?

The broadcasts overview describes context receivers as registered between registration and unregistration, subject to the registering context's validity. Application-context registration lasts with the running app; it does not create a subscription that survives process death.

Choose the smallest scope that meets the requirement. If an Activity needs a hint only while started, pair registration with onStart() and unregistration with onStop(). If it needs hints while resumed, choose that narrower pair. These are requirement-driven choices, not a rule that every listener belongs in onCreate().

Here is an illustrative app-only sketch using AndroidX Core. renderHint is a prompt UI update, not a network upload:

private val hints = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action == "example.SYNC_HINT") renderHint()
    }
}

override fun onStart() {
    super.onStart()
    ContextCompat.registerReceiver(
        this, hints, IntentFilter("example.SYNC_HINT"),
        ContextCompat.RECEIVER_NOT_EXPORTED
    )
}

override fun onStop() {
    unregisterReceiver(hints)
    super.onStop()
}

The receiver instance is stable across the matching calls. In more complex code, record successful registration and make teardown match that instance; do not hide an invalid lifecycle behind a blanket catch. Also explain threading if delivery uses a custom scheduler.

This sketch is not a complete Android project or a device-tested sample. Its interview value is the explicit scope and the separation between listening and doing durable work.

Repair the original registration trace

Assume a screen registers an app-only receiver in onStart() using application context, stores a reference to the screen, and unregisters only in onDestroy(). The requirement is to update the screen only while started. Trace the mismatch before changing code.

At T0, screen A starts and registers receiver A. At T1, the user opens another screen: A stops, but the receiver remains registered. At T2, a hint arrives and receiver A still updates or retains A. At T3, A starts again and attempts another registration. The subscription has outlived its required scope, and repeated entry can create confusing registration bookkeeping.

The smallest repair is a matched start/stop contract, with no Activity reference retained by a longer-lived registration. If the actual requirement changes to collecting app-wide hints, put that listener under an application-owned component and publish state through a repository. The screen then observes state only during its own lifecycle.

A third requirement changes the answer again: “the upload must continue after process loss.” Neither the repaired screen listener nor the application listener establishes that property. Persist the work intent and use a suitable durable scheduler.

Test the requirement by moving through start, stop, start and stop again, injecting a hint at each point. The expected number of screen callbacks is zero while stopped. Also test two screen instances so that destroying A cannot accidentally unregister B's subscription.

What does goAsync actually promise?

The BroadcastReceiver API says goAsync() keeps broadcast processing active after onReceive() returns, using a PendingResult. Processing still has a responsiveness deadline, and the interval until finish() counts toward it. This is bounded callback processing, not a persistent job.

An off-thread implementation must call finish() after processing, including failure paths. Plan for submission rejection as well as exceptions inside the submitted task; otherwise you can obtain a pending result without any worker that will finish it. A finally inside a task does not handle a task that never starts.

Do not say that a background thread guarantees completion if the process disappears. Likewise, launching a coroutine does not automatically select a background dispatcher, create a durable record, or extend the receiver's lifetime correctly. State the scope and completion owner.

For a short operation, an original reasoning sequence is: validate the action, capture only required data, acquire the pending result, submit bounded work, finish on success or failure, and finish immediately if submission fails. Long or unpredictable network activity belongs behind a different execution contract.

A forgotten finish() and an overloaded executor can both manifest as slow broadcast processing, but they require different repairs. Measure queue delay separately from execution time instead of assuming the callback's own code is the entire delay.

How do you hand work off without promising exactly once?

WorkManager's persistent-task guidance covers work intended to continue across app restarts and device reboots. Scheduling is subject to constraints and operating-system policy; “durable” does not mean “runs immediately after every hint.” Select it because its execution contract fits, not because it is a replacement for every event listener.

For an original upload exercise, let the receiver record an upload ID and schedule a worker that reads durable application data. Keep the broadcast payload small and make the worker recheck account state. A hint can be duplicated or become obsolete while the job waits.

The WorkRequest guidance describes inputs, constraints and retry/backoff. A retryable network failure is different from an invalid permanent input. Explain how the worker distinguishes them and how the UI reflects pending, failed and completed work.

Scheduling and server success are separate events. If a request commits remotely but the client loses its response, a retry can repeat the attempt. Use a stable operation ID and server-side idempotency when the effect requires deduplication. Unique local work naming alone does not prove exactly-once external execution.

The receiver should be an entry point into that design, not the owner of an Activity or an unbounded in-memory queue. Draw the persisted record, scheduler and remote effect as distinct boundaries.

Which tests would expose an incomplete answer?

Use negative tests that correspond to the three contracts. For authorization, send a valid-looking custom broadcast from an unauthorized test app and check that it causes no protected effect. For lifecycle, send after onStop() and check that the stopped screen receives no callback. For execution, terminate the process after a durable work record exists and verify recovery without duplicating the intended remote effect.

Distinguish platform timeouts from a universal interview stopwatch. Android's ANR diagnosis guide shows broadcast timeout differences by OS version and foreground/background priority, including CPU-starvation considerations. Ask which deadline applies; do not promise every receiver has exactly ten seconds on every device.

Record action, registration owner, enqueue result, worker ID and completion state without logging private payloads. If a hint is absent, first prove that registration was active and the action was eligible. If a callback arrived but the upload is absent, move the investigation to persistence, scheduling and worker execution.

Changing every receiver to exported and extending every listener to application scope can make a happy-path test pass while weakening both contracts. A convincing repair explains why the old failure can no longer occur under the stated assumptions.

Sources and Further Reading

Documentation checked October 4, 2026. Platform behavior depends on OS, target SDK, broadcast action and registration API; confirm the contract for your application.