Content Providers & Broadcasts Quiz
ANDROID › Components
Your app stores data only for its own use and never shares it. Which is the best choice?
- Implement a ContentProvider to keep the data properly abstracted away
- Use Room or another local store directly; no ContentProvider needed
- Expose the SQLite file through a FileProvider for in-app access
- Always wrap each database in a ContentProvider by Android guideline
Answer: Use Room or another local store directly; no ContentProvider needed
ContentProviders are mainly justified for cross-app sharing, search suggestions, widgets, or sync adapters. For app-private data, Room/DataStore is simpler and the provider adds needless overhead.
What does ContentResolver.insert() return on success?
- The number of rows inserted, returned as an Int
- A boolean value that simply indicates success
- The content URI for the newly inserted row
- A Cursor already positioned on the inserted row
Answer: The content URI for the newly inserted row
insert() returns the Uri of the new row (e.g. content://authority/table/id); query() returns a Cursor, while update() and delete() return row-count Ints.
In a ContentResolver query, what does the projection parameter specify?
- The WHERE clause filtering rows
- The ORDER BY sort order
- The set of columns to return
- The table (authority) to read from
Answer: The set of columns to return
Projection is the array of column names to return (the SELECT columns). Selection/selectionArgs form the WHERE clause and sortOrder is the ORDER BY.
Since Android 8.0 (API 26), what is true about manifest-declared receivers for implicit broadcasts?
- They still receive all implicit broadcasts, just like before Android 8.0
- Most implicit broadcasts no longer hit manifest receivers, except a few
- They need LocalBroadcastManager to work, instead of manifest registration
- They only work when the app targets API 21 or lower for broadcast delivery
Answer: Most implicit broadcasts no longer hit manifest receivers, except a few
API 26 removed most implicit broadcasts from manifest receivers to reduce background wakeups; a short exemption list (e.g. ACTION_BOOT_COMPLETED) remains. Use context-registered receivers or scheduled jobs instead.
Which capability is unique to an ordered broadcast (sendOrderedBroadcast) versus a normal one?
- It is delivered faster to all receivers at the same time
- A receiver can abort delivery or pass modified result data on
- It can be received without needing any IntentFilter at all
- It bypasses the Android 13+ exported-flag requirement entirely
Answer: A receiver can abort delivery or pass modified result data on
Ordered broadcasts go to one receiver at a time in priority order, letting each propagate or modify result data or call abortBroadcast(). Normal broadcasts run in undefined order with no result sharing or aborting.
On Android 13+ you context-register a receiver but omit the export flag. What happens?
- It silently defaults to not-exported, quietly blocking any outside senders from it
- It throws because RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED is required
- It defaults to exported for backward compatibility with older apps
- It only fails when the app is built with the Jetpack Compose UI
Answer: It throws because RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED is required
Targeting API 33+, registering a runtime receiver requires explicitly passing RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED (via ContextCompat.registerReceiver); omitting it raises an exception.
A BroadcastReceiver needs to do a few seconds of background work in onReceive(). What is the correct approach?
- Start a raw Thread and return; the system will keep the process alive
- Block inside onReceive() until the work completes, then let it return
- Use goAsync(), work off the main thread, then call finish() within ~10s
- Launch an Activity from onReceive() and use it to run the background work
Answer: Use goAsync(), work off the main thread, then call finish() within ~10s
onReceive() runs on the main thread and the process can be killed once it returns, so raw threads are unsafe. goAsync() keeps the broadcast alive while you work on a background thread, finishing within about 10 seconds; longer work belongs in WorkManager/JobScheduler.
By convention, what MIME type should a ContentProvider's getType() return for a URI that matches multiple rows (a directory)?
- A string starting with vnd.android.cursor.dir/
- A string starting with vnd.android.cursor.item/
- text/plain
- application/json
Answer: A string starting with vnd.android.cursor.dir/
The Android convention is vnd.android.cursor.dir/ for a URI matching many rows and vnd.android.cursor.item/ for a single row; getType() returns these so callers know whether the URI is a collection or one item.
You want to let another app read one specific image URI from your provider just for the duration of an Intent, without exposing your whole provider. What is the correct mechanism?
- Declare READ_EXTERNAL_STORAGE in the other app's manifest
- Permanently set android:exported="true" on your provider
- Have the other app request a runtime permission at first launch
- Add FLAG_GRANT_READ_URI_PERMISSION to the Intent
Answer: Add FLAG_GRANT_READ_URI_PERMISSION to the Intent
Temporary, per-URI access is granted by flagging the Intent with FLAG_GRANT_READ_URI_PERMISSION (and the provider allowing grantUriPermissions); the grant lasts only as long as the receiving component's stack, avoiding broad permanent exposure.
How does a client get notified when the data behind a content URI changes, rather than re-querying on a timer?
- Subscribe to a Flow that ContentResolver returns automatically for every single URI
- Register a BroadcastReceiver for a built-in ACTION_PROVIDER_CHANGED broadcast
- Register a ContentObserver; the provider calls notifyChange() after each write
- Override onContentChanged() in the calling Activity to catch updates
Answer: Register a ContentObserver; the provider calls notifyChange() after each write
Clients register a ContentObserver against the URI, and the provider must call ContentResolver.notifyChange() after modifying data so observers fire; ContentResolver does not expose an automatic Flow per URI.
Why do libraries such as Jetpack App Startup, Firebase, and WorkManager use a stub ContentProvider for auto-initialization?
- ContentProviders always run in a separate process, isolating the init work
- A provider's onCreate() runs before Application.onCreate(), enabling early auto-init
- ContentProviders uniquely survive configuration changes, unlike the other app components
- Only ContentProviders are permitted to read manifest <meta-data> tags
Answer: A provider's onCreate() runs before Application.onCreate(), enabling early auto-init
The system instantiates manifest-declared providers and calls their onCreate() before Application.onCreate(), so a zero-data stub provider is a convenient hook to run startup initialization automatically via manifest merging.
You send an app-wide broadcast but want only apps that hold a specific permission to be able to receive it. What is the correct approach?
- Pass the permission string as receiverPermission in sendBroadcast()
- Add FLAG_RECEIVER_REGISTERED_ONLY to the Intent before broadcasting it
- Use LocalBroadcastManager.sendBroadcast() to send the app-wide message out
- Set android:exported="false" on every receiver in the app manifest
Answer: Pass the permission string as receiverPermission in sendBroadcast()
sendBroadcast(intent, receiverPermission) limits delivery to receivers whose apps hold the named permission. FLAG_RECEIVER_REGISTERED_ONLY only restricts to runtime-registered receivers, and exported=false blocks other apps entirely rather than gating by permission.
LocalBroadcastManager is deprecated. What is the recommended modern way to deliver app-internal events between components in the same process?
- Convert each internal event into a manifest-declared receiver
- Use sendOrderedBroadcast() while keeping it inside your app
- Register a global BroadcastReceiver with RECEIVER_NOT_EXPORTED
- Use a shared Flow/StateFlow or LiveData observable
Answer: Use a shared Flow/StateFlow or LiveData observable
LocalBroadcastManager was deprecated in favor of observable data holders; an app-scoped StateFlow/SharedFlow or LiveData lets components communicate in-process without the broadcast machinery and its lifecycle pitfalls.
You implement a ContentProvider used only inside your own app. What is the best way to keep other apps from accessing it?
- Set android:grantUriPermissions="false" on the provider
- Nothing; providers are inaccessible to other apps by default on every Android version
- Set android:exported="false" on the <provider> element
- Set android:syncable="false" on the provider
Answer: Set android:exported="false" on the <provider> element
Explicitly setting android:exported="false" keeps the provider private to your app. Relying on defaults is unsafe because providers defaulted to exported=true before API 17, and grantUriPermissions/syncable do not control general cross-app access.