Android Custom Content Provider Introduction

Android content provider is used to share data between different android apps. Generally android app data is saved in SQLite database tables. Use content provider, you can choose which table or view’s data to be shared with other apps. This can make your android app data more safety.

1. Android Custom Content Provider Overview.

The commonly architecture of android app data sharing is as below. From the picture we can see that if you want to create a custom content provider which can expose your app data to others, you should do following things.

  1. Design your app SQLite database table, make sure which table or view data you want to share.
  2. Create custom content provider class that extends class android.content.ContentProvider. And configure it in AndroidManifest.xml file ( This can be done automatically if you use android studio).
  3. Override android.content.ContentProvider‘s onCreate, insert, update, delete and query method to implement SQLite database CRUD operation.

android app share data architecture

2. How To Create Custom Content Provider Class In Android Studio.

  1. Right click the package folder in android studio left project panel. Commonly the last folder name in the package path is provider.
  2. Click New —> Other —> Content Provider menu.
    android studio new custom content provider menu
  3. Input class name and uri authorities. The uri authority is used in content uri and uri mimetype. Check both exported and enabled checkbox. Enabled means this content provider is working while Exported means this content provider can be accessed by other apps.
    custom content provider class detail info panel
  4. Click Finish button, then you can see below custom content provider class has been generated.
    public class AccountContentProvider extends ContentProvider {
        @Override
        public boolean onCreate() {
            return false;
        }
    
        @Nullable
        @Override
        public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
            return null;
        }
    
        @Nullable
        @Override
        public String getType(@NonNull Uri uri) {
            return null;
        }
    
        @Nullable
        @Override
        public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
            return null;
        }
    
        @Override
        public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
            return 0;
        }
    
        @Override
        public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
            return 0;
        }
    }
  5. You can also find AndroidManifest.xml file add below custom content provider configuration settings. Please remember the value in android:authorities attribute, it will be used later.
    <provider
        android:name=".datasharing.custom.provider.AccountContentProvider"
        android:authorities="com.dev2qa.account.provider"
        android:enabled="true"
        android:exported="true"></provider>

3. Custom Android Content Provider Override Methods Introduction.

3.1 onCreate Method.

This method is invoked when the content provider is initialized. We usually execute app SQLite database table create and upgrade actions in this method.

If the action is success without error, onCreate method will return true, otherwise it will return false.

Please note that the content provider is initialized only when the ContentResolver tries to access it use the content provider provided Uri.

3.2 Cursor query(Uri uri, String []columns, Stirng whereClause, Stirng []whereClausePlaceHolderValue, String orderBy).

This method will be invoked when client app content resolver want to query this content provider shared data.

  1. Uri uri : Used to specify which table to query. The uri string format is commonly like ‘content://com.d…m.provider/table1′ ( means query for entire table1 ) or ‘content://com.d…m.provider/table1/1’ ( means query for the row which _id column value is 1 ).
  2. String []columns : Specify which column’s value that will be returned to client content resolver.
  3. Stirng whereClause : This is the query condition that is used to filter the query result. It is similar with where clause in sql command.
  4. Stirng []whereClausePlaceHolderValue : If you use place holder ( ? ) character in where clause, then this is the place holder string value arrays. If you do not use place holder in where clause, this parameter value should be null.
  5. String orderBy : This is the order by clause in the query like sql command.
  6. The return value should be an android.database.Cursor object. Client app content resolver class will use below code to loop the result in the cursor.
    if(cursor!=null)
    {
        // Get the result count.
        int queryResultCount = cursor.getCount();
        // below check can avoid cursor index out of bounds exception. android.database.CursorIndexOutOfBoundsException
        if(queryResultCount > 0)
        {
            // Move to the first row in the result cursor.
            cursor.moveToFirst();
            do{
               ......
               long rawContactId = cursor.getLong(cursor.getColumnIndex(ContactsContract.RawContacts._ID));
            }while(cursor.moveNext())      
        }
    }

3.3 Uri insert(Uri uri, ContentValues contentValues).

Insert one row data into content provider, and return a Uri object that is used to represent this newly inserted record.

  1. Uri uri : Specify which table that the data will be inserted into.
  2. ContentValues contentValues : Store the column name and values that will be instered.
    // Insert data into content provider example code.
    
    // Prepare content values.
    ContentValues contentValues = new ContentValues();
    contentValues.put("name", "jerry");
    contentValues.put("notes", "He is a experience android developer.");
    
    // insert content values into employee table
    Uri uri = contentResolver.insert(Uri.parse("content://com.dev2qa.provider/employee"), contentValues);
    
    // Get the newly created employee id use the returned uri object.
    long employeeId = ContentUris.parseId(uri);

3.4 int delete(Uri uri, String whereClause, String[] whereClausePlaceHolderValues).

Delete the rows in uri specified SQLite table.

  1. Uri uri : Specify which table data row that will be deleted.
  2. String whereClause : Delete rows condition, like sql where caluse, you can use place holder character ( ? ) in it.
  3. String[] whereClausePlaceHolderValues : This is the place holder value arrays if you use place holder in where clause, it can also be null that means there is no place holder in where clause.
  4. The return value is the count of deleted rows.
    // Invoke content provider delete method example code.
    
    ContentResolver contentResolver = getContentResolver();
    
    contentResolver.delete(Uri.parse("content://com.dev2qa.provider/employee"), " name = 'jerry' ", null);

3.5 int update(Uri uri, ContentValues contentValues, String whereClause, String[] whereClausePlaceHolderValues).

Update exist row column values in uri specified table. The first two input parameters is similar with insert method, and the last two input parameters is similar with delete method. The return value is the updated rows count.

// Create content values object.
ContentValues contentValues = new ContentValues();

// Put new employee notes.
contentValues.put("notes", "This guy is a very good android developer.");

// Create query condition, query with the employee name.
StringBuffer whereClauseBuf = new StringBuffer();

whereClauseBuf.append("name = 'jerry' ");

// Update employee notes values by name condition and return update row count..
int updateCount = contentResolver.update(Uri.parse("content://com.dev2qa.provider/employee"), contentValues, whereClauseBuf.toString(), null);

3.6 String getType(Uri uri)

This method return uri object data mimetype. One uri map to one mimetype. The mimetype value is a string value that has below three parts.

  1. Start with vnd.
  2. If the uri ends with path then add android.cursor.dir/ after vnd. If the uri ends with table row id then add android.cursor.item/.
  3. Then add vnd.<authority>.<path> at the end of the string. authority value is configured in Android Manifest xml file. path value is table name in general.
READ :   Android Contacts Database Structure

So for a uri such as content://com.dev2qa.provider/employee, it’s mimetype value is vnd.android.cursor.dir/vnd.com.dev2qa.provider.employee.

For a uri such as content://com.dev2qa.provider/employee/1, it’s mimetype value is vnd.android.cursor.item/vnd.com.dev2qa.provider.employee.

4. Content URI.

Content uri is passed by ContentResolver, it will tell content provider which table ( by table name ) or which table row (by table name and row _id ) need to be operated ( such as insert, update, delete and query ).

Below are standard content uri examples.

content://<authority>/table_name or content://<authority>/table_name/row_id.

  1. content://com.dev2qa.provider/employee : This means the caller want to access all the data in employee table.
  2. content://com.dev2qa.provider/employee/1 : This means the caller want to access data row which _id columns’s value is 1 in employee table.

You can also use wildcard to match above two type of uri.

  1. * : Match any characters of any length.
  2. # : Match any integer numbers.

So you can create uri like below.

  1. content://com.dev2qa.provider/* : Means caller need to access any table.
  2. content://com.dev2qa.provider/employee/# : Means caller want to access all rows in employee table.

5. How To Parse Content Resolver Passed Uri.

One of your custom content provider’s function is to parse content resolver passed uri to know which table or rows that client app want to access. You can use android.content.UriMatcher class to achieve this goal as below.

5.1 First create a UriMatcher instance without matches input.

private static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

5.2 Then invoke UriMatcher’s addURI() method to add some custom content provider uri in it.

addURI() method has below three input parameters.

  1. Content uri authority.
  2. Content uri path ( can use wildcard ( * or # )).
  3. User defined code.

This means you can control which data to share to other apps by uri, this can make data security more stronger and avoid important data leak risk.

public static final int EMPLOYEE_DIR = 0;

public static final int EMPLOYEE_ITEM = 1;

public static final int MANAGER_DIR = 2;

public static final int MANAGER_ITEM = 3;

static{
    uriMatcher.addURI("com.dev2qa.provider", "employee", EMPLOYEE_DIR);

    uriMatcher.addURI("com.dev2qa.provider", "employee/#", EMPLOYEE_ITEM);

    uriMatcher.addURI("com.dev2qa.provider", "manager", MANAGER_DIR); 

    uriMatcher.addURI("com.dev2qa.provider", "manager/#", MANAGER_ITEM);
}

5.3 Use UriMatcher’s match() method to parse and return client app passed uri’s user defined code.

With user defined code, you can know which table or table row that client content resolver want to access.

public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {

    // Parse uri and return user defined code.
    int userDefinedCode = uriMatcher.match(uri);
    
    if(userDefinedCode==EMPLOYEE_DIR)
    {
        // Operate employee table all row data.
    }else if(userDefinedCode==EMPLOYEE_ITEM)
    {
        // Access employee table single row.
    }else if(userDefinedCode==MANAGER_DIR)
    {
        // Process manager table all row data.
    }else if(userDefinedCode==MANAGER_ITEM)
    {
        // Process manager table single row.
    }
(Visited 521 times, 1 visits today)

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.