Content Providers in Android

Content Providers in Android

What Is a Content Provider?

A Content Provider acts as an interface for applications to access and modify data from other applications or databases securely. They use a uniform API, so developers don’t have to worry about the underlying data source. A Content Provider is accessed through a Content Resolver, which performs queries, updates, inserts, and deletes on the data.

Key Concepts in Content Providers

  • Projection: Refers to selecting which columns you would like to include in your question. You can think of it as filtering the fields you need from a set of data.
  • Selection: This is the filtering mechanism, i.e., the WHERE clause in SQL. It allows you to filter the results based on conditions like matching certain column values.
  • Sorting: You can order the results of a query using the ORDER BY clause. This helps in retrieving data in a specific order (ascending or descending).


1. Getting Contacts Using Content Provider

Let's start by querying contacts using the ContactsContract content provider.

Example: Fetching Contact Names and Phone Numbers

To get contact details, you can use the ContactsContract content provider to access contacts stored on the device.

val contentResolver = context.contentResolver

val projection = arrayOf(
    ContactsContract.Contacts.DISPLAY_NAME,
    ContactsContract.CommonDataKinds.Phone.NUMBER
)

val selection = "${ContactsContract.Contacts.HAS_PHONE_NUMBER} = 1"
val sortOrder = "${ContactsContract.Contacts.DISPLAY_NAME} ASC"

val cursor: Cursor? = contentResolver.query(
    ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
    projection,
    selection,
    null,
    sortOrder
)

cursor?.let {
    while (it.moveToNext()) {
        val name = it.getString(it.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
        val phoneNumber = it.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
        println("Name: $name, Phone Number: $phoneNumber")
    }
    it.close()
}        

Explanation:

  • Projection: We only query the display name and phone number.
  • Selection: We ensure that we only fetch contacts that have a phone number (HAS_PHONE_NUMBER = 1).
  • Sorting: We sort the results by name in ascending order (DISPLAY_NAME ASC).


2. Getting Images and Sorting by Date

Next, let's retrieve images from the device’s media store and sort them by date (from last month).

Example: Fetching Images Sorted by Date

Next, let's retrieve images from the device’s media store and sort them by date (from last month).

val contentResolver = context.contentResolver

val projection = arrayOf(
    MediaStore.Images.Media._ID,
    MediaStore.Images.Media.DATE_ADDED
)

val oneMonthAgo = System.currentTimeMillis() - (30L * 24 * 60 * 60 * 1000)  // 30 days ago
val selection = "${MediaStore.Images.Media.DATE_ADDED} >= ?"
val selectionArgs = arrayOf(oneMonthAgo.toString())
val sortOrder = "${MediaStore.Images.Media.DATE_ADDED} DESC"  // Sort by most recent

val cursor: Cursor? = contentResolver.query(
    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
    projection,
    selection,
    selectionArgs,
    sortOrder
)

cursor?.let {
    while (it.moveToNext()) {
        val imageId = it.getLong(it.getColumnIndex(MediaStore.Images.Media._ID))
        val dateAdded = it.getLong(it.getColumnIndex(MediaStore.Images.Media.DATE_ADDED))
        println("Image ID: $imageId, Date Added: $dateAdded")
    }
    it.close()
}        

Explanation:

  • Projection: We query the image ID and the date it was added.
  • Selection: We filter images added in the last month.
  • Sorting: The results are sorted DATE_ADDED in descending order, showing the most recent first.


3. Creating a Custom Content Provider for Room Database

Now, let’s create a custom content provider for a Room Database. This lets your app expose its data to other apps through a content provider.

Example: Creating a Content Provider for a Room Database

class NotesContentProvider : ContentProvider() {

    private lateinit var notesDao: NotesDao

    override fun onCreate(): Boolean {
        val db = Room.databaseBuilder(context!!, AppDatabase::class.java, "notes.db").build()
        notesDao = db.notesDao()
        return true
    }

    override fun query(
        uri: Uri, 
        projection: Array<String>?, 
        selection: String?, 
        selectionArgs: Array<String>?, 
        sortOrder: String?
    ): Cursor? {
        val cursor = notesDao.getAllNotesCursor()
        return cursor
    }

    override fun insert(uri: Uri, values: ContentValues?): Uri? {
        val id = notesDao.insert(NoteEntity(values?.getAsString("title")!!, values.getAsString("content")!!))
        return Uri.withAppendedPath(uri, id.toString())
    }

    override fun update(
        uri: Uri, 
        values: ContentValues?, 
        selection: String?, 
        selectionArgs: Array<String>?
    ): Int {
        val count = notesDao.updateNote(
            values?.getAsLong("id")!!, 
            values.getAsString("title")!!, 
            values.getAsString("content")!!
        )
        return count
    }

    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
        val count = notesDao.deleteNote(selectionArgs?.get(0)?.toLong() ?: -1)
        return count
    }

    override fun getType(uri: Uri): String? {
        return "vnd.android.cursor.dir/vnd.com.example.notes"
    }
}        

Explanation:

  • This custom provider interacts with a Room database and uses its DAO to perform operations.
  • You define methods like query(), insert(), update(), and delete() for CRUD operations.


4. Creating a Custom Content Provider for SQLite Database

Finally, let’s create a custom content provider that interacts with a SQLite database.

Example: Content Provider for SQLite Database

class SQLiteNotesContentProvider : ContentProvider() {

    private lateinit var dbHelper: SQLiteOpenHelper

    override fun onCreate(): Boolean {
        dbHelper = NotesDbHelper(context!!)
        return true
    }

    override fun query(
        uri: Uri,
        projection: Array<String>?,
        selection: String?,
        selectionArgs: Array<String>?,
        sortOrder: String?
    ): Cursor? {
        return dbHelper.readableDatabase.query(
            NotesContract.NotesEntry.TABLE_NAME,
            projection,
            selection,
            selectionArgs,
            null,
            null,
            sortOrder
        )
    }

    override fun insert(uri: Uri, values: ContentValues?): Uri? {
        val id = dbHelper.writableDatabase.insert(NotesContract.NotesEntry.TABLE_NAME, null, values)
        return Uri.withAppendedPath(uri, id.toString())
    }

    override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<String>?): Int {
        return dbHelper.writableDatabase.update(
            NotesContract.NotesEntry.TABLE_NAME,
            values,
            selection,
            selectionArgs
        )
    }

    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
        return dbHelper.writableDatabase.delete(
            NotesContract.NotesEntry.TABLE_NAME,
            selection,
            selectionArgs
        )
    }

    override fun getType(uri: Uri): String? {
        return "vnd.android.cursor.dir/vnd.com.example.notes"
    }
}        

Explanation:

  • This provider uses a custom SQLite database helper class (NotesDbHelper), which is responsible for managing database connections and performing CRUD operations.
  • Similar to the Room database example, the query(), insert(), update(), and delete() methods are implemented.


Conclusion

In this article, we explored content providers in Kotlin for Android, covering how to use them to retrieve contacts and images, as well as how to create custom providers for both Room and SQLite databases. The key concepts of projection, selection, and sorting were explained with examples to help you understand how to manipulate data efficiently.

These providers are useful when you need to expose or share data between different apps, or if you want to allow access to data from different sources in your app.

要查看或添加评论,请登录

Abdelrhman Ghanem的更多文章

  • Understanding URIs in Android

    Understanding URIs in Android

    A Uniform Resource Identifier (URI) is a string of characters uniquely identifying a resource. In Android, URIs are…

  • WorkManager in Android

    WorkManager in Android

    What is WorkManager? WorkManager is an API Android Jetpack provides for scheduling deferrable, asynchronous tasks that…

  • Foreground Services in Android

    Foreground Services in Android

    A Foreground Service is an Android service that performs a task while actively notifying the user, generally through a…

  • Broadcasts and Broadcast Receivers in Android

    Broadcasts and Broadcast Receivers in Android

    Broadcasts in Android allow applications and the Android system to send messages across apps and system components…

  • Intents and Intent Filters in Android

    Intents and Intent Filters in Android

    What is an Intent? An in Android is a messaging object used to request actions from other components like activities…

  • Android Resources and Qualifiers

    Android Resources and Qualifiers

    When building an Android app, you want it to look and work well across all devices. To do that, Android gives us…

  • Context in Android

    Context in Android

    What is Context? In Android, represents the current state of the application. It provides access to various resources…

  • Understanding Configuration Changes and ViewModel in Android

    Understanding Configuration Changes and ViewModel in Android

    1. What Are Configuration Changes? Configuration changes occur when the device environment changes in a way that…

  • Android Back Stack, Tasks, and Launch Modes

    Android Back Stack, Tasks, and Launch Modes

    In Android, managing screen navigation and app flows effectively depends on understanding the backstack, tasks, and…

  • Android Activity & Fragment Lifecycle

    Android Activity & Fragment Lifecycle

    In Android development, Activities and Fragments are essential components that help create engaging user interfaces…

社区洞察

其他会员也浏览了