The Android framework uses a concept called content providers to enable applications to share and use data across the platform. A content provider is an owner of particular content, it provides well defined APIs to read, insert, update and delete that data. The content provider can internally use any place to store its data like a local file, local database or some remote service.
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 MyContentProvider extends ContentProvider {
}
Content providers create an abstraction layer between its repository of data and external application that are using data. External Application can call ContentProvider methods with the use of ContentResolver. ContentResolver work as ContentProvider client object, with the use of ContentResolver object we can get data from ContentProvider.
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>
Here are the details of various parts of the URI
Contacts content provider, then the data path would be people and URI would look like this content://contacts/people.Contacts content provider then URI would look like this content://contacts/people/5.A ContentProvider usually has 3 components, a constants class that defines the schema for your tables and your content authority, an SQLite helper class that physically creates the table, and the provider class, in which you implement the URiMatcher and your CRUD methods.
Creating of ContentProvider involves number of simple steps to create your own content provider.
ContentProvider class that extends the ContentProvider base class.onCreate() method which will use SQLite Open Helper method to create or open the provider's database. When your application is launched, the onCreate() handler of each of its content providers is called on the main application thread.ContentProvider queries to perform different database specific operations.ContentProvider in your activity file using <provider> tag.Here is the list of methods which you need to override in ContentProvider class to have your ContentProvider working.

onCreate() method is called when the provider is started.query() method receives a request from a client. The result is returned as a Cursor object.insert() method inserts a new record into the content provider.delete() method deletes an existing record from the content provider.update() method updates an existing record from the content provider.getType() method returns the MIME type of the data at the given URI.To retrieve the data, we need to follow these step
Following example will explain you how to create your own ContentProvider.
First, modify main activity file MainActivity.java to add two new methods addMovie() and getMovies().
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void addMovie(View view) {
// add a new movie record
String title = ((EditText)findViewById(R.id.etTitle)).getText().toString();
int year = Integer.valueOf(((EditText)findViewById(R.id.etYear)).getText().toString());
ContentValues values = new ContentValues();
values.put(MovieProvider.TITLE, title);
values.put(MovieProvider.YEAR, year);
Uri uri = getContentResolver().insert(MovieProvider.CONTENT_URI, values);
Toast.makeText(getBaseContext(), uri.toString(), Toast.LENGTH_LONG).show();
}
public void getMovies(View view) {
// retrieve movie records
String URL = "content://" + MovieProvider.PROVIDER_NAME;
Uri moviesUri = Uri.parse(URL);
String orderBy = MovieProvider.TITLE;
Cursor c = getContentResolver().query(moviesUri, null, null, null, orderBy);
if (c.moveToFirst()) {
String id, title, year;
do {
id = c.getString(c.getColumnIndex(MovieProvider.ID));
title = c.getString(c.getColumnIndex(MovieProvider.TITLE));
year = c.getString(c.getColumnIndex(MovieProvider.YEAR));
Toast.makeText(this, id + ", " + title + ", " + year, Toast.LENGTH_SHORT).show();
} while (c.moveToNext());
}
}
}
Create a new java file called MovieProvider.java to define your actual provider and associated methods. Inside of our MovieProvider class, we need to define a few properties: a content authority (PROVIDER_NAME), which is a unique identifier for our database and a base URI (CONTENT_URI).
public class MovieProvider extends ContentProvider {
static final String PROVIDER_NAME = "me.proft.MovieProvider";
static final String URL = "content://" + PROVIDER_NAME + "/movies";
static final Uri CONTENT_URI = Uri.parse(URL);
static final String ID = "id";
static final String TITLE = "title";
static final String YEAR = "year";
private static HashMap<String, String> MOVIES_PROJECTION_MAP;
static final int MOVIES = 1;
static final int MOVIE_ID = 2;
static final UriMatcher uriMatcher;
static{
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(PROVIDER_NAME, "movies", MOVIES);
uriMatcher.addURI(PROVIDER_NAME, "movies/#", MOVIE_ID);
}
// database specific constant declarations
private SQLiteDatabase db;
static final String DATABASE_NAME = "Movie";
static final String TABLE_NAME = "movies";
static final int DATABASE_VERSION = 1;
static final String CREATE_DB_TABLE =
" CREATE TABLE " + TABLE_NAME +
" (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
" title TEXT NOT NULL, " +
" year INTEGER NOT NULL);";
// helper class that actually creates and manages the provider's underlying data repository.
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_DB_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}
@Override
public boolean onCreate() {
Context context = getContext();
DatabaseHelper dbHelper = new DatabaseHelper(context);
// create a write able database which will trigger its creation if it doesn't already exist.
db = dbHelper.getWritableDatabase();
return (db == null)? false:true;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// add a new movie record
long rowID = db.insert(TABLE_NAME, "", values);
// if record is added successfully
if (rowID > 0) {
Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
getContext().getContentResolver().notifyChange(_uri, null);
return _uri;
}
throw new SQLException("Failed to add a record into " + uri);
}
@Override
public Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(TABLE_NAME);
switch (uriMatcher.match(uri)) {
case MOVIES:
qb.setProjectionMap(MOVIES_PROJECTION_MAP);
break;
case MOVIE_ID:
qb.appendWhere(ID + "=" + uri.getPathSegments().get(1));
break;
default:
}
if (sortOrder == null || sortOrder == ""){
// by default sort on movie title
sortOrder = TITLE;
}
Cursor c = qb.query(db, projection, selection,
selectionArgs,null, null, sortOrder);
// register to watch a content URI for changes
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
switch (uriMatcher.match(uri)){
case MOVIES:
count = db.delete(TABLE_NAME, selection, selectionArgs);
break;
case MOVIE_ID:
String id = uri.getPathSegments().get(1);
count = db.delete(TABLE_NAME, ID + " = " + id + (!TextUtils.isEmpty(selection) ? "
AND (" + selection + ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public int update(Uri uri, ContentValues values,
String selection, String[] selectionArgs) {
int count = 0;
switch (uriMatcher.match(uri)) {
case MOVIES:
count = db.update(TABLE_NAME, values, selection, selectionArgs);
break;
case MOVIE_ID:
count = db.update(TABLE_NAME, values, ID + " = " + uri.getPathSegments().get(1) +
(!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri );
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)){
// get all movie records
case MOVIES:
return "vnd.android.cursor.dir/vnd.me.proft.movies";
// get a particular student
case MOVIE_ID:
return "vnd.android.cursor.item/vnd.me.proft.movies";
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
}
}
To deal with multiple URIs Android provides the helper class UriMatcher. This class eases the parsing of URIs.
The query() method takes in five parameters:
uri. The URI (or table) that should be queried.projection. A string array of columns that will be returned in the result set.selection. A string defining the criteria for results to be returned.selectionArgs. Arguments to the above criteria that rows will be checked against.sortOrder. A string of the column(s) and order to sort the result set by.The insert() method takes in a ContentValues object, which is a key value pair of column names and values to be inserted.
The update() and delete() methods take in a selection string and arguments to define which rows should be updated or deleted. They differ in that the update method requires a ContentProvider object as well, for the columns in that row(s) that will be updated.
The getType method is used to find the MIME type of the results, either a directory of multiple results, or an individual item.
Register your content provider in your AndroidManifest.xml file using
<manifest ...
<application ...
<provider android:name="MovieProvider" android:authorities="me.proft.MovieProvider"/>
</application>
</manifest>
Modify the default content of activity_main.xml file.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="2dp">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Title"
android:id="@+id/etTitle"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Year"
android:id="@+id/etYear"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnAdd"
android:text="Add movie"
android:onClick="addMovie"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnGetMovie"
android:text="Get movie"
android:onClick="getMovies"/>
</LinearLayout>
Run the application to launch Android emulator and verify the result of the changes done in the application.
