Content Providers

A content provider component supplies data from one application to others on request. Such requests are handled by the methods of the ContentResolver class. A content provider can use different ways to store its data and the data can be stored in a database, in files, or even over a network.


Content providers let you centralize content in one place and have many different applications access it as needed. A content provider behaves very much like a database where you can query it, edit its content, as well as add or delete content using insert(), update(), delete(), and query() methods. In most cases this data is stored in an SQlite database.

A content provider is implemented as a subclass of ContentProvider class and must implement a standard set of APIs that enable other applications to perform transactions.

public class MyApplication extends ContentProvider {
}

Content URIs

To query a content provider, you specify the query string in the form of a URI which has following format

<prefix>://<authority>/<data_type>/<id>

A content URI pattern matches content URIs using wildcard characters:

  • *: Matches a string of any valid characters of any length.
  • #: Matches a string of numeric characters of any length.

As an example of designing and coding content URI handling, consider a provider with the authority com.example.app.provider that recognizes the following content URIs pointing to tables:

  • content://com.example.app.provider/table1: A table called table1.
  • content://com.example.app.provider/table2/dataset1: A table called dataset1.
  • content://com.example.app.provider/table2/dataset2: A table called dataset2.
  • content://com.example.app.provider/table3: A table called table3.

Here is the detail of various parts of the URI −

Part Description
prefix This is always set to content://
authority This specifies the name of the content provider, for example contacts, browser etc. For third-party content providers, this could be the fully qualified name, such as com.theitbulls.androidtutorial
data_type This indicates the type of data that this particular provider provides. For example, if you are getting all the contacts from the Contacts content provider, then the data path would be people and URI would look like this content://contacts/people
id This specifies the specific record requested. For example, if you are looking for contact number 5 in the Contacts content provider then URI would look like this content://contacts/people/5

Here is the list of methods which you need to override in Content Provider class to have your Content Provider working −

Method Description
onCreate() This method is called when the provider is started.
query() This method receives a request from a client. The result is returned as a Cursor object.
insert() This method inserts a new record into the content provider.
delete() This method deletes an existing record from the content provider.
update() This method updates an existing record from the content provider.
getType() This method returns the MIME type of the data at the given URI.

This example will explain you how to create your own ContentProvider. Let's create a project for insert, list, delete, or update Employee data

EmployeeProvider.java
package com.theitbulls.contentproviderex;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;

import com.theitbulls.contentproviderex.room.AppDatabase;
import com.theitbulls.contentproviderex.room.Employee;
import com.theitbulls.contentproviderex.room.EmployeeDAO;

import org.jetbrains.annotations.NotNull;

public class EmployeeProvider extends ContentProvider {
    private static final String AUTHORITY = "com.theitbulls.contentproviderex";
    public static final String DATA_TYPE = "employee";
    public static final String URL = "content://" + AUTHORITY + "/" + DATA_TYPE;
    public static final Uri CONTENT_URI = Uri.parse(URL);

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

    private static final int EMPLOYEE = 1;
    private static final int EMP_ID = 2;

    static {
        uriMatcher.addURI(AUTHORITY, DATA_TYPE, EMPLOYEE);
        uriMatcher.addURI(AUTHORITY, DATA_TYPE + "/#", EMP_ID);
    }

    public EmployeeProvider() {
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        switch (uriMatcher.match(uri)) {
            case EMPLOYEE:
                throw new IllegalArgumentException("Invalid URI, cannot update without ID" + uri);
            case EMP_ID:
                Context mContext = getContext();
                if(mContext == null) {
                    return -1;
                }

                long rowId = ContentUris.parseId(uri);
                return AppDatabase.getInstance(mContext).dao().delete(rowId);
        }
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public String getType(@NotNull Uri uri) {
        switch (uriMatcher.match(uri)) {
            case EMPLOYEE:
                return "vnd.android.cursor.dir/" + AUTHORITY + "." + EmployeeDAO.TABLE_NAME;
            case EMP_ID:
                return "vnd.android.cursor.item/" + AUTHORITY + "." + EmployeeDAO.TABLE_NAME;
            default:
                throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        switch (uriMatcher.match(uri)) {
            case EMPLOYEE:
                try {
                    Context mContext = getContext();
                    if(mContext == null) {
                        return null;
                    }

                    Employee emp = Employee.getEmployee(values);
                    long rowId = AppDatabase.getInstance(mContext).dao().insert(emp);

                    Uri mUri = ContentUris.withAppendedId(CONTENT_URI, rowId);
                    mContext.getContentResolver().notifyChange(mUri, null);
                    return mUri;
                } finally {
                }
            case EMP_ID:
                throw new IllegalArgumentException("Invalid URI, cannot insert with ID: " + uri);
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
    }

    @Override
    public boolean onCreate() {
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        final int code = uriMatcher.match(uri);
        if (code == EMPLOYEE || code == EMP_ID) {
            final Context context = getContext();
            if (context == null) {
                return null;
            }

            EmployeeDAO dao = AppDatabase.getInstance(context).dao();
            final Cursor cursor;
            if (code == EMPLOYEE) {
                cursor = dao.selectAll();
            } else {
                cursor = dao.selectById(ContentUris.parseId(uri));
            }

            cursor.setNotificationUri(context.getContentResolver(), uri);
            return cursor;

        }
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        switch (uriMatcher.match(uri)) {
            case EMPLOYEE:
                throw new IllegalArgumentException("Invalid URI, cannot update without ID" + uri);
            case EMP_ID:
                final Context context = getContext();
                if (context == null) {
                    return 0;
                }

                Employee employee = Employee.getEmployee(values);
                AppDatabase.getInstance(context).dao().insert(employee);
                context.getContentResolver().notifyChange(uri, null);
                return 0;

        }
        throw new UnsupportedOperationException("Not yet implemented");
    }
}


MainActivity.java
package com.theitbulls.contentproviderex;

import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatButton;
import androidx.appcompat.widget.AppCompatEditText;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.theitbulls.contentproviderex.room.Employee;

import java.util.List;

public class MainActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    private AppCompatEditText etEmpId;
    private AppCompatEditText etEmpName;
    private AppCompatEditText etEmpSalary;
    private AppCompatEditText etEmpType;

    private boolean toUpdate;
    private Employee employee;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);

        etEmpId = findViewById(R.id.empId);
        etEmpName = findViewById(R.id.empName);
        etEmpSalary = findViewById(R.id.empSalary);
        etEmpType = findViewById(R.id.empType);


        LinearLayoutManager llm = new LinearLayoutManager(this);
        llm.setOrientation(LinearLayoutManager.VERTICAL);

        recyclerView = findViewById(R.id.rvEmpList);
        recyclerView.setLayoutManager(llm);

        AppCompatButton btnAdd = findViewById(R.id.btnAdd);
        btnAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                try {
                    Employee employee = getEmployee();
                    ContentValues values = Employee.getContentValues(employee);
                    new InsertItemTask().execute(values);
                } catch (Exception e) {
                    Toast.makeText(MainActivity.this, "Record added error: " + e.toString(), Toast.LENGTH_SHORT).show();
                }
            }
        });

        initEmplList();
    }

    private void initEmplList() {
        new ListItemTask().execute();
    }

    public void edit(Employee employee) {
        this.employee = employee;

        etEmpId.setText(employee.empId);
        etEmpName.setText(employee.name);
        etEmpSalary.setText(String.valueOf(employee.salary));
        etEmpType.setText(String.valueOf(employee.type));
    }

    public void delete(long id) {
        String url = EmployeeProvider.URL + "/" + id;
        Uri uri = Uri.parse(url);

        new DeleteItemTask().execute(uri);
    }

    private Employee getEmployee() {
        Employee employee = new Employee();
        employee.empId = etEmpId.getText().toString();
        employee.name = etEmpName.getText().toString();

        String salary = etEmpSalary.getText().toString();
        employee.salary = Double.parseDouble(salary);

        String type = etEmpType.getText().toString();
        employee.type = Integer.parseInt(type);
        return employee;
    }

    public class ListItemTask extends AsyncTask<Void, Void, List<Employee>> {
        @Override
        protected List<Employee> doInBackground(Void... voids) {
            Cursor cursor = getContentResolver().query(EmployeeProvider.CONTENT_URI, null, null, null, null);
            return Employee.getEmployee(cursor);
        }

        @Override
        protected void onPostExecute(final List<Employee> employees) {
            if (employees == null || employees.isEmpty()) {
                Toast.makeText(MainActivity.this, "No record found.", Toast.LENGTH_SHORT).show();
                return;
            }

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    EmployeeListAdapter adapter = new EmployeeListAdapter(MainActivity.this, employees);
                    recyclerView.setAdapter(adapter);

                    adapter.notifyDataSetChanged();
                }
            });
        }
    }

    public class InsertItemTask extends AsyncTask<ContentValues, Void, Uri> {
        @Override
        protected Uri doInBackground(ContentValues... values) {
            return getContentResolver().insert(EmployeeProvider.CONTENT_URI, values[0]);
        }

        @Override
        protected void onPostExecute(final Uri uri) {
            if (uri == null) {
                Toast.makeText(MainActivity.this, "Record could not added.", Toast.LENGTH_SHORT).show();
                return;
            }

            initEmplList();
        }
    }

    public class DeleteItemTask extends AsyncTask<Uri, Void, Integer> {
        @Override
        protected Integer doInBackground(Uri... values) {
            return getContentResolver().delete(values[0], null, null);
        }

        @Override
        protected void onPostExecute(Integer integer) {
            if (integer > -1) {
                Toast.makeText(MainActivity.this, "Record deleted successfully.", Toast.LENGTH_SHORT).show();
            }
            initEmplList();
        }
    }

    public class UpdateItemTask extends AsyncTask<ContentValues, Void, Integer> {
        private Uri uri;

        public UpdateItemTask(Uri uri) {
            this.uri = uri;
        }

        @Override
        protected Integer doInBackground(ContentValues... values) {
            return getContentResolver().update(uri, values[0], null, null);
        }

        @Override
        protected void onPostExecute(final Integer count) {
            if (count < 0) {
                Toast.makeText(MainActivity.this, "Record could not updated.", Toast.LENGTH_SHORT).show();
                return;
            }

            employee = null;
            initEmplList();
        }
    }
}


Following will the modified content of AndroidManifest.xml file. Here we have added <provider.../> tag to include our content provider:

<provider
            android:name=".EmployeeProvider"
            android:authorities="com.theitbulls.contentproviderex"
            android:enabled="true"
            android:exported="true" />

Working Application Screen shot

Download: ContentProviderExample.zip