Content Provider in Android

Content Provider in Android

A content provider manages access to a central repository of data. A provider is part of an Android application, which often provides its own UI for working with the data. However, content providers are primarily intended to be used by other applications, which access the provider using a provider-client object.

How to use a Content provider in Application

We use content providers for accessing, sharing the data among the applications.

1- Accessing the Provider

When you want to access data in a content provider, you use the ContentResolver object in your application’s Context to communicate with the provider as a client.

2- Register the provider class in Manifest.xml

<provider

   android:authorities="learnprogramming.academy.tasktimer.provider"

   android:name="learnprogramming.academy.tasktimer.AppProvider"

   android:exported="false"/>

 

3- Create a model class for the data

@Parcelize

data class Task(val name: String, val description: String, val sortOrder: Int, var id: Long = 0) : Parcelable {

}

 

4- Create an object class 

object TasksContract {

   internal const val TABLE_NAME = "Tasks"

   /**

    * The URI to access the Tasks table.

    */

   val CONTENT_URI: Uri = Uri.withAppendedPath(CONTENT_AUTHORITY_URI, TABLE_NAME)


   const val CONTENT_TYPE = "vnd.android.cursor.dir/vnd.$CONTENT_AUTHORITY.$TABLE_NAME"

   const val CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.$CONTENT_AUTHORITY.$TABLE_NAME"



   // Tasks fields

   object Columns {

       const val ID = BaseColumns._ID

       const val TASK_NAME = "Name"

       const val TASK_DESCRIPTION = "Description"

       const val TASK_SORT_ORDER = "SortOrder"

   }

   fun getId(uri: Uri): Long {

       return ContentUris.parseId(uri)

   }

   fun buildUriFromId(id: Long): Uri {

       return ContentUris.withAppendedId(CONTENT_URI, id)

   }

}

 

5- Create a provider class

const val CONTENT_AUTHORITY = "learnprogramming.academy.tasktimer.provider"

val CONTENT_AUTHORITY_URI: Uri = Uri.parse("content://$CONTENT_AUTHORITY")

private const val TASKS = 100

private const val TASKS_ID = 101

class AppProvider: ContentProvider() {

   private val uriMatcher by lazy { buildUriMatcher() }

   private fun buildUriMatcher() : UriMatcher {

       Log.d(TAG, "buildUriMatcher: starts")

       val matcher = UriMatcher(UriMatcher.NO_MATCH)

       // e.g. content://learnprogramming.academy.tasktimer.provider/Tasks

       matcher.addURI(CONTENT_AUTHORITY, TasksContract.TABLE_NAME, TASKS)

       // e.g. content://learnprogramming.academy.tasktimer.provider/Tasks/8

       matcher.addURI(CONTENT_AUTHORITY, "${TasksContract.TABLE_NAME}/#", TASKS_ID)

       return matcher

   }

 override fun onCreate(): Boolean {

       Log.d(TAG, "onCreate: starts")

       return true

   }

   override fun getType(uri: Uri): String {

       val match = uriMatcher.match(uri)

       return when (match) {

           TASKS -> TasksContract.CONTENT_TYPE

           TASKS_ID -> TasksContract.CONTENT_ITEM_TYPE

           else -> throw IllegalArgumentException("unknown Uri: $uri")

       }

   }


   override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?,

                      sortOrder: String?): Cursor {

       Log.d(TAG, "query: called with uri $uri")

       val match = uriMatcher.match(uri)

       Log.d(TAG, "query: match is $match")

       val queryBuilder = SQLiteQueryBuilder()

       when (match) {

           TASKS -> queryBuilder.tables = TasksContract.TABLE_NAME

           TASKS_ID -> {

               queryBuilder.tables = TasksContract.TABLE_NAME

               val taskId = TasksContract.getId(uri)

               queryBuilder.appendWhere("${TasksContract.Columns.ID} = ")

               queryBuilder.appendWhereEscapeString("$taskId")

           }

           else -> throw IllegalArgumentException("Unknown URI: $uri")

       }

       val db = AppDatabase.getInstance(context).readableDatabase

       val cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder)

       Log.d(TAG, "query: rows in returned cursor = ${cursor.count}") // TODO remove this line

       return cursor

   }

   override fun insert(uri: Uri, values: ContentValues): Uri {

       Log.d(TAG, "insert: called with uri $uri")

       val match = uriMatcher.match(uri)

       Log.d(TAG, "insert: match is $match")

       val recordId: Long

       val returnUri: Uri

       when(match) {

           TASKS -> {

               val db = AppDatabase.getInstance(context).writableDatabase

               recordId = db.insert(TasksContract.TABLE_NAME, null, values)

               if(recordId != -1L) {

                   returnUri = TasksContract.buildUriFromId(recordId)

               } else {

                   throw SQLException("Failed to insert, Uri was $uri")

               }

           }

           else -> throw IllegalArgumentException("Unknown uri: $uri")

       }

       if (recordId > 0) {

           // something was inserted

           Log.d(TAG, "insert: Setting notifyChange with $uri")

           context?.contentResolver?.notifyChange(uri, null)

       }

       Log.d(TAG, "Exiting insert, returning $returnUri")

       return returnUri

   }

   override fun update(uri: Uri, values: ContentValues, selection: String?, selectionArgs: Array<out String>?): Int {

       Log.d(TAG, "update: called with uri $uri")

       val match = uriMatcher.match(uri)

       Log.d(TAG, "update: match is $match")

       val count: Int

       var selectionCriteria: String

       when(match) {

           TASKS -> {

               val db = AppDatabase.getInstance(context).writableDatabase

               count = db.update(TasksContract.TABLE_NAME, values, selection, selectionArgs)

           }

           TASKS_ID -> {

               val db = AppDatabase.getInstance(context).writableDatabase

               val id = TasksContract.getId(uri)

               selectionCriteria = "${TasksContract.Columns.ID} = $id"

               if(selection != null && selection.isNotEmpty()) {

                   selectionCriteria += " AND ($selection)"

               }

               count = db.update(TasksContract.TABLE_NAME, values, selectionCriteria, selectionArgs)

           }

           else -> throw IllegalArgumentException("Unknown uri: $uri")

       }

       if (count > 0) {

           // something was updated

           Log.d(TAG, "update: Setting notifyChange with $uri")

           context?.contentResolver?.notifyChange(uri, null)

       }

       Log.d(TAG, "Exiting update, returning $count")

       return count

   }

   override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {

       Log.d(TAG, "delete: called with uri $uri")

       val match = uriMatcher.match(uri)

       Log.d(TAG, "delete: match is $match")

       val count: Int

       var selectionCriteria: String

       when(match) {

           TASKS -> {

               val db = AppDatabase.getInstance(context).writableDatabase

               count = db.delete(TasksContract.TABLE_NAME, selection, selectionArgs)

           }

             TASKS_ID -> {

               val db = AppDatabase.getInstance(context).writableDatabase

               val id = TasksContract.getId(uri)

               selectionCriteria = "${TasksContract.Columns.ID} = $id"

               if(selection != null && selection.isNotEmpty()) {

                   selectionCriteria += " AND ($selection)"

                }

        count = db.delete(TasksContract.TABLE_NAME, selectionCriteria, selectionArgs)

           }

                  else -> throw IllegalArgumentException("Unknown uri: $uri")

       }

       if (count > 0) {

           // something was deleted

           Log.d(TAG, "delete: Setting notifyChange with $uri")

           context?.contentResolver?.notifyChange(uri, null)

       }

              Log.d(TAG, "Exiting delete, returning $count")

       return count

   }

}

 

6- When we want to store the data in the content provider

fun saveTask(task: Task): Task {

   val values = ContentValues()

   if (task.name.isNotEmpty()) {

       // Don't save a task with no name

       values.put(TasksContract.Columns.TASK_NAME, task.name)

       values.put(TasksContract.Columns.TASK_DESCRIPTION, task.description)

       values.put(TasksContract.Columns.TASK_SORT_ORDER, task.sortOrder)  // defaults to zero if empty


       if (task.id == 0L) {

           GlobalScope.launch {

               Log.d(TAG, "saveTask: adding new task")

               val uri = getApplication<Application>().contentResolver?.insert(TasksContract.CONTENT_URI, values)

               if (uri != null) {

                   task.id = TasksContract.getId(uri)

                   Log.d(TAG, "saveTask: new id is ${task.id}")

               }

           }

       } else {

           // task has an id, so we're updating

           GlobalScope.launch {

               Log.d(TAG, "saveTask: updating task")

               getApplication<Application>().contentResolver?.update(TasksContract.buildUriFromId(task.id), values, null, null)

           }

       }

   }

   return task

}

 

7- When we want to fetch the data using a content provider

private fun loadTasks() {

   val projection = arrayOf(TasksContract.Columns.ID,

           TasksContract.Columns.TASK_NAME,

           TasksContract.Columns.TASK_DESCRIPTION,

           TasksContract.Columns.TASK_SORT_ORDER)

   // <order by> Tasks.SortOrder, Tasks.Name

   val sortOrder = "${TasksContract.Columns.TASK_SORT_ORDER}, ${TasksContract.Columns.TASK_NAME}"


GlobalScope.launch {

   val cursor = getApplication<Application>().contentResolver.query(

           TasksContract.CONTENT_URI,

           projection, null, null,

           sortOrder)

   databaseCursor.postValue(cursor)

   }

}

 

8- When we want to insert new data in a content provider

fun saveTask(task: Task): Task {

   val values = ContentValues()


   if (task.name.isNotEmpty()) {

       // Don't save a task with no name

       values.put(TasksContract.Columns.TASK_NAME, task.name)

       values.put(TasksContract.Columns.TASK_DESCRIPTION, task.description)

       values.put(TasksContract.Columns.TASK_SORT_ORDER, task.sortOrder)  // defaults to zero if empty


       if (task.id == 0L) {

           GlobalScope.launch {

               Log.d(TAG, "saveTask: adding new task")

               val uri = getApplication<Application>().contentResolver?.insert(TasksContract.CONTENT_URI, values)

               if (uri != null) {

                   task.id = TasksContract.getId(uri)

                   Log.d(TAG, "saveTask: new id is ${task.id}")

               }

           }

       } else {

           // task has an id, so we're updating

           GlobalScope.launch {

               Log.d(TAG, "saveTask: updating task")

               getApplication<Application>().contentResolver?.update(TasksContract.buildUriFromId(task.id), values, null, null)

           }

       }

   }

   return task

}

 

9- When we want to delete the task

fun deleteTask(taskId: Long) {

   Log.d(TAG, "Deleting task")

   GlobalScope.launch {

       getApplication<Application>().contentResolver?.delete(TasksContract.buildUriFromId(taskId), null, null)

   }

}

 

 

Leave a Reply