Android Content Providers Explained - Complete Developer Guide
In this tutorial, you'll learn how Android Content Providers enable data sharing between apps with a standardized interface, using practical Kotlin examples.
What You'll Learn
how Android Content Providers enable data sharing between apps with a standardized interface, using practical Kotlin examples — Content Providers are the standard way to share structured data between Android apps. They underpin system access to contacts, media, documents, and calendar events.
Why It Matters
Content Providers are the standard way to share structured data between Android apps. They underpin system access to contacts, media, documents, and calendar events.
Real-World Use
A contact backup app queries the Contacts provider to read contacts, serializes them to vCard, and writes to external storage via MediaStore. A custom provider exposes backup status to other apps.
Learning Path
flowchart LR
[Data Storage] --> [Content Providers] --> [Room Database] --> [MediaStore]
style 2 fill:#4CAF50,color:#fff
Querying Contacts Provider
val projection = arrayOf(ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME)
contentResolver.query(
ContactsContract.Contacts.CONTENT_URI, projection, null, null,
"${ContactsContract.Contacts.DISPLAY_NAME} ASC"
)?.use { cursor ->
while (cursor.moveToNext()) {
val id = cursor.getLong(cursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID))
val name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME))
Log.d("Contacts", "ID: $id, Name: $name")
}
}
Expected output: The cursor iterates through contacts, extracting ID and display name. The use block auto-closes the cursor.
Writing to MediaStore
val values = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, "screenshot.png")
put(MediaStore.Images.Media.MIME_TYPE, "image/png")
put(MediaStore.Images.Media.RELATIVE_PATH, "${Environment.DIRECTORY_PICTURES}/DodaTech")
}
val uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
uri?.let {
contentResolver.openOutputStream(it)?.use { outputStream ->
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
}
}
Expected output: The image is saved to the device gallery under Pictures/DodaTech with proper metadata.
Custom Content Provider
class BooksProvider : ContentProvider() {
private lateinit var dbHelper: DatabaseHelper
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH)
companion object {
const val AUTHORITY = "com.dodatech.provider"
const val BOOKS = 1; const val BOOKS_ID = 2
}
override fun onCreate(): Boolean {
dbHelper = DatabaseHelper(context)
uriMatcher.addURI(AUTHORITY, "books", BOOKS)
uriMatcher.addURI(AUTHORITY, "books/#", BOOKS_ID)
return true
}
override fun query(uri: Uri, projection: Array<String>?, selection: String?,
selectionArgs: Array<String>?, sortOrder: String?): Cursor? {
val db = dbHelper.readableDatabase
return when (uriMatcher.match(uri)) {
BOOKS -> db.query("books", projection, selection, selectionArgs, null, null, sortOrder)
BOOKS_ID -> db.query("books", projection, "_id = ?", arrayOf(uri.lastPathSegment), null, null, null)
else -> throw IllegalArgumentException("Unknown URI: $uri")
}
}
}
Expected output: The custom provider routes queries to the correct database operation based on URI matching.
Common Errors
- Cursor not closed - always use .use {} or close in finally block; leaked cursors crash the app
- SecurityException for system providers - Contacts and Calendar require runtime permissions
- Invalid URI format - use UriMatcher to validate content URIs match expected patterns
- Recursive query in custom provider - don't call getContentResolver().query() inside your provider
- MIME type mismatch - returning wrong MIME type breaks third-party integration
Practice Questions
What are the four parts of a Content URI?
How does UriMatcher help route requests in a custom provider?
Why must Content Providers declare their MIME type?
What permission is needed to write to MediaStore on Android 10+?
How does a Content Provider differ from a simple database helper?
Challenge
Build a notes provider: create a ContentProvider for a notes app with CRUD operations. Build a second app that reads notes via ContentResolver. The provider should only allow read access to other apps.
Real-World Task
Create a file-sharing provider using FileProvider that exposes app-internal document files. Configure the provider in AndroidManifest.xml with file_paths.xml mapping to a documents directory.
Frequently Asked Questions
{{< faq question="Can a Content Provider work across processes?">}} Yes, that's its primary purpose. Content Providers use IPC to share data across app boundaries. {{< /faq >}}
{{< faq question="Do I need a Content Provider if I only use Room?">}} No. Room works directly via DAOs. Providers are only needed to share data with other apps or widgets. {{< /faq >}}
{{< faq question="What is DocumentsProvider?">}} DocumentsProvider extends ContentProvider for Storage Access Framework (SAF) integration, letting users browse files from your app. {{< /faq >}}
Security Tip: Limit your provider's exported permissions. Use android:permission on the provider tag to require a specific permission, or use android:grantUriPermissions for temporary access grants.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro