How to Use Content Provider in Android.

Nyame Bismark
8 min readNov 27, 2020
Photo by Danielle MacInnes on Unsplash

Content provider manages the access to repository of data. Provider is part of android application components. The primary intention of content provider to allow other application to have access to provider through a provider client object. The provider and provider clients combination helps to give secure data access and handling of inter-process communication.

Scenarios in which content providers come handy is basically two ways. First, we use content providers to access the content or data of other applications and secondly, we can create a content provider that gives access or share our application data to other application.

Ways to Use Content Provider

a. You want to expose your application data to widget.
b. You want to implement custom search suggestion in your application
c. You want to copy and paste a complex data or file from your application to other application.

Content provider provides a secured access to structured data like SQLite relational databases and unstructured data like audio, video, images and personal contacts.

Relationship between content provider and other components.

Accessing a Provider

When we need to access a content provider, we make use of ContentResolver object within your application context. The contentResolver communicates with the provider, an instance of the class that implements contentProvider. The provider object receives data request from the client and perform the request action on behalf of the client (which is the ContentResolver) and deliver the results back to the client.

This object has methods that call identically-named methods in the provider object, an instance of one of the concrete subclasses of ContentProvider. The ContentResolver methods provide the basic "CRUD" (create, retrieve, update, and delete) functions of persistent storage.

Pattern for Accessing Content Provider

Let’s use cursorLoader to generate a pattern in accessing a contentProvider from UI. The UI (Fragment or the Activity) calls the cursorLoader which in turns calls the ContentProvider through the contentResolver and the result is returned to the UI. This pattern allows the UI to continue to be available to the user while the query is running.

Interaction between ContentProvider, other classes, and storage.

How is Content Provider Used?

In our quest to understand the content providers, let’s start by using a content provider to retrieve data from User Dictionary tables. From the above pattern of how content providers work, we intend to follow the pattern and use the content provider likewise to retrieve data from the UserDictionary tables.

Before we can retrieve data from the UserDictionary table like word table, we need the content URI that points to the word table. The ContentURI follows a pattern, the first part is content://, which is the scheme, and the second part is the authority, which uniquely identifies the content provider, and the last part is the path to the table.

The ContentResolver which is the client of the contentProvider uses the authority to uniquely identify the a particular content provider to dispatch the request to. Using this content URI to access the first record of Word table, we could simply do this by appending the particular ID to the last part of the content uri.

//the content uri of the words table in USERDICTIONARY.
content://user_dictionary/words
//Accessing a record of the words table with ID 1
Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,1);

Before we start reading records from the table, we need a permission to do that and this can be done in the androidManifest.xml as done below.

<uses-permission android:name="android.permission.READ_USER_DICTIONARY"/>

Querying for a data

Now after the permission added in the android manifest file, we can read a record with the contentResolver#query() method which accepts some arguments.

Inserting a data

To insert data into a provider, you call contentResolver#insert() to add a new row to the provider and returns a content URI for that row.

Updating a data

To update a data, we use contentValues with the updated values just as you did with a insertion data and the selection criteria like we did with a query. We use contentResolver#update() in order to update a contentValues.

Deleting data

To delete a data in the provider, we call contentResolver#delete() to delete the selected item which satisfies the selected criteria.

Creating A Content Provider

Now, we already know that content provider manages the access to data repository. We create a class that implements ContentProvider which is an interface between your provider and other applications. Content providers are meant to share data to other applications but you can also make your activities and fragments query and modify data managed by your provider.

Before we start with the creation of the content provider, we need to create our data store or repository from which the content provider will manage the data access. Let’s go ahead and create our database, and you can create database using android Room database, Sugar DB, or normal SQLite database which we will use in our example.

Creating the SQLite Database helper and this is going to help us create, upgrade and manage our database. Now we create DBHelper.java class.

Let’s create our content provider contract class that will kind of describe the table the content provider is managing. Let’s call the contract class BookContract.java.

Now, we can create our content provider to manage our data store. We create a class called BookProvider.java which extends ContentProvider class.

Implementing the OnCreate Method of Content Provider

When content provider is implemented by a custom class, it implements some methods. One of them is onCreate() method. This is where we define our initialization or it shows when our content provider is getting created. We define content provider in AndroidManifest and as a result, it is part of the elements that get created when the application is started. So if we define complex task in the onCreate() method hook, we stand the risk of the application delaying its start, so wisely initialise items in the onCreate(). Here we are going to initialise our DBHelper class here.

private DBHelper dbHelper;
private SQLiteDatabase database;

@Override
public boolean onCreate() {
//initialize our database helper here
dbHelper = new DBHelper(getContext());
database = dbHelper.getWritableDatabase();

//Please, do not do complex task here lest
// your content provider will be very slow

return true;
}

Implementing the getType Method of Content Provider

Content provider provides the content type of its supported URIs. This method takes the Uri and returns you the string indicating the content type.

@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch (URI_MATCHER.match(uri)){
case ITEM_ID:
return BookContract.BookColumn.CONTENT_ITEM_TYPE;
case ITEM_LIST:
return BookContract.BookColumn.CONTENT_DIR_TYPE;
default:
throw new IllegalArgumentException("No Uri exist for "+ uri);

}
}

Implementing the insert Method of the Content Provider

Content provider provides this method hook to use for insertion of new row. During this implementation, we need to check for the Uri the client is using for insertion. During successful insertion, we also notify all listeners for the change.

@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {

//checking that the Uri is dir base type
if (URI_MATCHER.match(uri) == ITEM_ID)
throw new IllegalArgumentException("Unsupported Uri for insertion: " + uri);

long id = database.insert(DBHelper.BOOK_TABLE_NAME, null, values);

//check if new data inserted
if (id > 0) {
Uri newRowUri = ContentUris.withAppendedId(BookContract.BookColumn.CONTENT_URI, id);

//notify all listeners of the change
getContext().getContentResolver().notifyChange(newRowUri, null);
return newRowUri;
}

throw new SQLException("Problem inserting into this Uri: " + uri);
}

Implementing the query method of content provider

This method helps the client to query for any item within a particular table which is specified by the Uri. Then again, we need to check if the Uri is matching a single item or a list of items.

@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {

SQLiteQueryBuilder sqLiteQueryBuilder = new SQLiteQueryBuilder();
sqLiteQueryBuilder.setTables(DBHelper.BOOK_TABLE_NAME);

switch (URI_MATCHER.match(uri)) {
case ITEM_LIST:
if (TextUtils.isEmpty(sortOrder)) {
sortOrder = BookContract.BookColumn.SORT_ORDER_DEFAULT;
}
break;
case ITEM_ID:
sqLiteQueryBuilder.appendWhere(
BookContract.BookColumn._ID + " = "
+ uri.getLastPathSegment());
break;

default:
throw new IllegalArgumentException("Unsupported Uri: " + uri);
}

Cursor cursor = sqLiteQueryBuilder.query(database, projection, selection,
selectionArgs, null, null, sortOrder);

if (cursor != null) {
cursor.setNotificationUri(getContext().getContentResolver(), uri);
}

return cursor;
}

Implementing update method of the content provider

This method is provided to update our data in the our table. We need to check the Uri passed from the client in order to know how to update our table.

@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
int updatedCount = 0;
switch (URI_MATCHER.match(uri)) {
case ITEM_LIST:
updatedCount = database.update(DBHelper.BOOK_TABLE_NAME, values, selection, selectionArgs);
break;
case ITEM_ID:
String id = uri.getLastPathSegment();
String where = BookContract.BookColumn._ID +
" = " + id;
if (!TextUtils.isEmpty(selection)) {
where += " AND " + selection;
}
updatedCount = database.update(DBHelper.BOOK_TABLE_NAME, values, where, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unsupported Uri: " + uri);

}

// notify all listeners of changes:
if (updatedCount > 0)
getContext().getContentResolver().notifyChange(uri, null);
return updatedCount;
}

Implementing delete method of content provider

This method is provided to help us delete our table data and we also need to check the Uri so to let us know what to do.

@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
int deletedCount = 0;
switch (URI_MATCHER.match(uri)){
case ITEM_LIST:
deletedCount = database.delete(DBHelper.BOOK_TABLE_NAME,selection,selectionArgs);
break;
case ITEM_ID:
String where = BookContract.BookColumn._ID +
" = " + uri.getLastPathSegment();
if (!TextUtils.isEmpty(selection)){
where += " AND " + selection;
}
deletedCount = database.delete(DBHelper.BOOK_TABLE_NAME,where,selectionArgs);
break;
default:
throw new IllegalArgumentException("Unsupported Uri for deletion: "+ uri);
}

if (deletedCount > 0){
getContext().getContentResolver().notifyChange(uri,null);
}
return deletedCount;
}

Putting the methods together.

Calling Our Provider in AndroidManifest file

This is to initialize our provider and it is called in the android manifest as we would for other android components.

<!--protecting our provider-->
<permission android:name="com.bisapp.mycontentprovider.BOOK_PROVIDER" />
<provider
android:name=".provider.BookProvider"
android:authorities="com.bisapp.mycontentprovider"
android:exported="true"
android:grantUriPermissions="true"
android:permission="com.bisapp.mycontentprovider.BOOK_PROVIDER" />

Using our Created Provider in Application

I have created an application using our content provider we created and if you want to see the implementation, please check this github repository. After implementing the content provider example in my github repository, you will see this interface and it is not nice but it works for the CRUD operations.😂

Thanks you all for staying up with me for this tutorials. Much love.😍References

https://developer.android.com/guide/topics/providers/content-provider-basics

--

--