Android Content Provider in Kotlin

Welcome, here we are going to learn about one of the android components: Content Provider.

What is a Content Provider?

When we need to access the data of other application or when we need to make our application data available for other applications to access securely, Content Provider comes in the scene. We have a class called ContentResolver, which helps us to manage the requests of data. The data can be a stored file, a database or maybe over a network. Content Provider responds with the data in the cursor format.

Let’s take a real-time example to understand the Content Provider. We all use WhatsApp application. The list of contact which we save get synced with WhatsApp. But, how the WhatsApp is accessing our contacts from our phones? The answer is — Using the Content Provider.

So, our contact application has a ContentProvider which shares it’s data to other application and WhatsApp request to the contact app to get access to the list of contacts. It makes the contact app a provider and WhatsApp as a client which will use the data provided by the provider.

Some of the essential terminologies of ContentProvider

  • Content URI (Uniform Resource Identifier): It is the URI that indicates the data in a provider. The URI looks like this:
  1. content://  :  It is a scheme of Content Provider which is always present.
  2. authority : It is a name of the Content Provider which is unique like browser, contacts etc.
  3. optionalPath: It is a path which helps to differentiate our data in the content provider.
  4. optionalId : It is a single record of a file that we want to access from the content provider. This optionId must be number.
  • query():  This method is from the Content Resolver class. This helps us to query the data and return a cursor. This method has some parameters:
  1. Projection : It is an array of the columns from the data table which we want to get in our result when we query the Content Provider.
  2. Selection:  It is a condition for selecting the rows from the data table.
  3. SortOrder:  It is the order of the data which returns when we query
mCursor = contentResolver.query(
        CONTENT_URI,   // The content URI of the words table
        Projection,    // The columns to return for each row
        SelectionClause,            // Selection criteria
        SelectionArgs.toTypedArray(),  // Selection criteria
        SortOrder     // The sort order for the returned rows
  • Cursor : The data which we will get after query the Content Provider, is in Cursor type. Cursor class helps the app to manage the information. Sometimes the cursor may be null, best practice to check the nullability:
// iterate the cursor
when (mCursor?.count) {
// iterate the cursor
mCursor?.apply {
   while (moveToNext()) { 
      // to iterate the cursor 
  • CursorLoader: This uses ContentProvider to run a query against the database and returns Cursor.
  • SimpleCursorAdapter: This helps to map the data from the cursor to UI.
  • ContentValues: This is used to store the set of values which ContentResolver class can process.
val newValues = ContentValues().apply {
    // Sets the values of each column and inserts the value. 
     // The arguments to the "put"
     // method are "column name" and "value"
  • Loaders: This helps to load the data from Content Provider to UI.
  • <Provider>: To declare that our app uses Content Provider we need to report it in a manifest file using this tag
  • Contract Class: This is a class where we need to define the constant definitions URIs, column names, MIME types, and other meta-data about the Content Provider. It can also contain static helper methods to manipulate the URIs.
  • Uri Matcher:  Which action to take on an incoming request (content URI), URI Matcher class plays a role here. This returns an integer value. A content URI pattern matches content URIs using these two characters:
  1. *: Matches a string of any valid characters of any length
  2. #: Matches a string of numeric characters of any length

Let’s develop a simple application where we will insert the data and query it using content resolver and display on the screen.

Firstly, we need to create the layout of our main screen where we will display our data.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=""
        app:layout_constraintTop_toTopOf="parent" />
        android:text="Display All"
        app:layout_constraintStart_toStartOf="parent" />
        android:text="Display First Item"
        app:layout_constraintVertical_bias="1.0" />

Add the list of string in the strings.xml file

<string-array name="sentence">

Now, we will create a contract class. All our variable should be final static, so as per Kotlin we are using a companion object.

class Contract  {
   companion object {
val AUTHORITY  =     "com.mindorks.mindorkscontentprovider.provider"
val CONTENT_PATH = "sentence"
val CONTENT_URI = Uri.parse("content://$AUTHORITY/$CONTENT_PATH")
       val ALL_ITEMS = -2
        val WORD_ID = "id"

Now, Let’s create our Content Provider class

class MindOrksContentProvider : ContentProvider() {
   var mData: Array<String>? = null
   private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH)
   override fun onCreate(): Boolean {
        val context = context
        mData = context!!.resources.getStringArray(R.array.sentence)
        return true
   private fun initializeUriMatching() {
        sUriMatcher.addURI(Contract.AUTHORITY, Contract.CONTENT_PATH + "/#", 1)
        sUriMatcher.addURI(Contract.AUTHORITY, Contract.CONTENT_PATH, 0)
   override fun query(uri: Uri, projection: Array<String>?, selection: String?,
                       selectionArgs: Array<String>?, sortOrder: String?): Cursor {
        var id = -1
        when (sUriMatcher.match(uri)) {
            0 -> {
                id = Contract.ALL_ITEMS
                if (selection != null) {
                    id = Integer.parseInt(selectionArgs!![0])
            1 -> id = parseInt(uri.getLastPathSegment())
            UriMatcher.NO_MATCH -> {
                id = -1
            else -> {
                id = -1
        return populateCursor(id)
   private fun populateCursor(id: Int): Cursor {
        val cursor = MatrixCursor(arrayOf(Contract.CONTENT_PATH))
        if (id == Contract.ALL_ITEMS) {
            for (i in 0 until mData!!.size) {
                val word = mData!![i]
        } else if (id >= 0) {
            val word = mData!![id]
        return cursor
   override fun getType(uri: Uri): String? {
        return null
   override fun insert(uri: Uri, values: ContentValues?): Uri? {
        return null
   override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
        return 0
   override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<String>?): Int {
        return 0

Let’s connect with our MainActivity

class MainActivity : AppCompatActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
   fun onClickDisplayEntries(view: View) {
        tvDisplayDataHere.text = ""
        var queryUri = Contract.CONTENT_URI.toString()
        var projection = arrayOf(Contract.CONTENT_PATH)
        var selectionClause: String?
        var selectionArgs: Array<String>? = null
        var sortOrder: String? = null
       when ( {
   -> {
                selectionClause = null
                selectionArgs = null
   -> {
                selectionClause = Contract.WORD_ID + " = ?"
                selectionArgs = arrayOf("0")
            else -> {
                selectionClause = null
                selectionArgs = null
       val cursor = contentResolver.query(Uri.parse(queryUri), projection, selectionClause,
                selectionArgs, sortOrder)
       if (cursor != null) {
            if (cursor.count > 0) {
                val columnIndex = cursor.getColumnIndex(projection[0])
                do {
                    val word = cursor.getString(columnIndex)
                    tvDisplayDataHere.append(word + "\n")
                } while (cursor.moveToNext())
            } else {
                tvDisplayDataHere.append("No data returned." + "\n")
        } else {
            tvDisplayDataHere.append("Cursor is null." + "\n")

Lastly, add the provider declaration to the manifest file

android:name=".MindOrksContentProvider" android:authorities="com.mindorks.mindorkscontentprovider.provider" />

Let’s run the application. Clicking on each button will give you the required result — great work.

Happy Learning :)

Team MindOrks!