Custom ContentProvider for Android – Part 1

custom contentprovider android

ContentProvider, accordingly to official tutorials from Android SDK team, is required only if your app needs to share data with other apps, or, for example, it should have a search across app’s data using search suggestions.

Part 2
Part 3

Nevertheless, you will need CursorAdapter along with Cursor Loader to display data, and this group requires ContentProvider. It helps you work with list and tables, because it automatically fetch data from SQLite DB and updates it in your views when necessary.

In this post I will tell you how to create convenient ContentProvider to interact with SQLite DB. First of all, we need to define DB structure. As an example, let’s take 2 tables: Students and Classes. Tables will look as follows:

Students

_id first_name second_name average_score fk_class_id

Classes

_id class_number class_letter

fk_class_id will be the foreign ket to class Classes and will keep value of Classes._id

Now when we know our DB scheme, you can create Contract Class – a class, that stores all column names, column projections and other default values. The class looks as follows:

public final class ContractClass {
	public static final String AUTHORITY = "org.nerdgrl.examples.contentproviderexample.provider.ContractClass";
	private ContractClass() {}
	public static final class Students implements BaseColumns {...}
	public static final class Classes implements BaseColumns {...}
}

where AUTHORITY – is a unique string amongst all apps, which will be used by other apps if you give them access your data from SQLite DB. It’s recommended to use domain name to prevent conflicts with other providers.

BaseColumns class is a simple class with set of predefined columns for SQLite, namely _ID and _COUNT. Though ContentProvider doesn’t require the primary key to be named as _ID, it IS required to work successfully with ListView. So, it’s easier to use BaseColumns class with these predefined columns.

Now let’s move to Students class:

public static final class Students implements BaseColumns {
	private Students() {}
	public static final String TABLE_NAME ="students";
	private static final String SCHEME = "content://";
	private static final String PATH_STUDENTS = "/students";
	private static final String PATH_STUDENTS_ID = "/students/";
	public static final int STUDENTS_ID_PATH_POSITION = 1;
	public static final Uri CONTENT_URI =  Uri.parse(SCHEME + AUTHORITY + PATH_STUDENTS);
	public static final Uri CONTENT_ID_URI_BASE = Uri.parse(SCHEME + AUTHORITY + PATH_STUDENTS_ID);
	public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.google.students";
	public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.google.students";
	public static final String DEFAULT_SORT_ORDER = "second_name ASC";
	public static final String COLUMN_NAME_FIRST_NAME   = "first_name";
	public static final String COLUMN_NAME_SECOND_NAME   = "second_name";
	public static final String COLUMN_NAME_AVERAGE_SCORE   = "average_score";
	public static final String COLUMN_NAME_FK_CLASS_ID   = "fk_class_id";
	public static final String[] DEFAULT_PROJECTION = new String[] {
		ContractClass.Students._ID,
		ContractClass.Students.COLUMN_NAME_FIRST_NAME,
		ContractClass.Students.COLUMN_NAME_SECOND_NAME,
		ContractClass.Students.COLUMN_NAME_AVERAGE_SCORE,
		ContractClass.Students.COLUMN_NAME_FK_CLASS_ID
	};
}

We can see here private default constructor method, which prevents incorrect usage of this class, because we only need constant values from it.

TABLE_NAME – table name in SQLite DB.

SCHEME – data scheme that is used, for example, in <intent-filter><data scheme=…></intent-filter>.

PATH_STUDENTS and PATH_STUDENTS_ID – used for constructing a ContentUri. ContentProvider uses ContentUri as a path to particular table or row.
Content Uri for Students table will look as follows:
content://org.nerdgrl.examples.contentproviderexample.provider.ContractClass/students (for all table)
content://org.nerdgrl.examples.contentproviderexample.provider.ContractClass/students/3 (for row #3)

CONTENT_TYPE and CONTENT_ITEM_TYPE – MIME types for our data. All user-defined data types should begin with vnd.android.cursor.dir/vnd for tables, and with vnd.android.cursor.item/vnd for particular rows.

Next we define default sorting order and all columns name for the table.

DEFAULT_PROJECTION – is a set of columns to include in query selection.

Code for Classes class:

public static final class Classes implements BaseColumns {
	private Classes() {}
	public static final String TABLE_NAME ="classes";
	private static final String SCHEME = "content://";
	private static final String PATH_CLASSES = "/classes";
	private static final String PATH_CLASSES_ID = "/classes/";
	public static final int CLASSES_ID_PATH_POSITION = 1;
	public static final Uri CONTENT_URI =  Uri.parse(SCHEME + AUTHORITY + PATH_CLASSES);
	public static final Uri CONTENT_ID_URI_BASE = Uri.parse(SCHEME + AUTHORITY + PATH_CLASSES_ID);
	public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.org.nerdgrl.classes";
	public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.org.nerdgrl.classes";
	public static final String DEFAULT_SORT_ORDER = "class_number ASC";
	public static final String COLUMN_NAME_CLASS_NUMBER   = "class_number";
	public static final String COLUMN_NAME_CLASS_LETTER   = "class_letter";
	public static final String[] DEFAULT_PROJECTION = new String[] {
		ContractClass.Classes._ID,
		ContractClass.Classes.COLUMN_NAME_CLASS_NUMBER,
		ContractClass.Classes.COLUMN_NAME_CLASS_LETTER
	};
}

In the next part I’ll tell you what ContentProvider consists of and how to use it to display data.

Full source code you can find here.

Leave a Reply

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