Android Content Providers

0
522

A content provider is an application component which is responsible for the supply of the requested data from one application to another. In other words, there are times when data sharing is required amongst the applications, this is where the Content Providers take charge. The requests for data by the applications are handled by the methods of the ContentResolver base class. This data can be stored in the database, files, or over a network.

Also Read: Android Services

Content providers have the capability of centralizing the data for various applications. These applications can access this data after requesting it from the content providers. This application component is similar to a database. You can insert, update, view, and delete any of the stored content with the help of the various methods. These methods can be insert(), update(), delete(), and query() methods. Generally, this content is stored in a database, SQLite.

Now, for the implementation of a content provider, you are required to extend the ContentProvider base class. Also, a set of APIs should be implemented so that other applications can perform required transactions.

public class MyApplication extends ContentProvider
{  }

Content URIs (Uniform Resource Identifier)
The query string for a content provider follows the below-provided format:

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

The detail of every part of the URI is provided in the table below.

S. No. Part of The URI Description
1 prefix * The value for prefix has to be fixed as content://.
2 authority * Authority indicates the name of the content provider.
* The example for the authority could be contacts, browsers, and others. But, in case of third-party content providers, the qualified value could be com.w3school.statusprovider.
3 data_type * This part of the URI carries the type of the data provided by every specific content provider. For example, if the Contacts content provider is requested for Contacts then the data path can be people and URI would be content://contacts/people.
4 id * This value indicates the id of the requested record. For example, if the Contacts content provider is requested for the Contact 5 from the Address Book then URi would be content://contacts/people/5.

Creation of Content Provider
In this section, we will be helping you to create your own content provider. Follow the given steps to create the content provider.

Step 1: Start by extending the ContentProvider base class for your content provider class.

Step 2: In here, define URI address for your content provider, so that this address can be used to access the requested content.

Step 3: After defining the URI address, create a database for your content. As mentioned earlier, Android uses SQLite database and here, you are required to override onCreate() method. This method will be used to create and open the database which is associated with the content provider.This can be done with the help of SQLite Open Helper method. After the launch of the Android application, the onCreate() method and the other handlers of the content provider is then called by the main application.

Step 4: Now, you can implement the database related operations by performing queries on it. You can perform these operations on our recently created content provider.

Step 5: At last, complete the process by registering your content provider in the activity file. This can be done by using the <provider> tag.

To make your content provider work, you are required to override a list of methods provided in the below figure.

S. No. Method Description
1 onCreate() * This method is called after the initiation of the content provider.
2 query() * This method returns a Cursor object after receiving a request from the client.
3 insert() * This method is responsible for the insertion of a new record in the content provider.
4 delete() * This method is responsible for the deletion of an existing record from the content provider.
5 update() * This method helps in updating or modifying an already existing record from the content provider.
6 getType() * This method returns MIME type of data. This value is returned at the provided URI.

Take a look at the following example where we are explaining the creation of your own content provider. You will be showcased with a step-to-step guide as we did in Hello World Example.

Step 1: Run the Android Studio IDE and create a new Android application. Save this application as MyApplication. This application should be kept under the com.example.MyApplication package.

Step 2: Now, you need to modify the MainActivity.java file by including two more methods in it. The path for this file would be src/com.example.MyApplication/MainActivity.java. These fundamental lifecycle methods are onClickAddName() method and onClickRetrieveStudents() method. Both of these methods take care of the interaction between the user and the Android application.

package com.example.MyApplication;
import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.ContentValues;
import android.content.CursorLoader;
import android.database.Cursor;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

public void onClickAddName(View view)
{
// Add a new student record
ContentValues values = new ContentValues();
values.put(StudentsProvider.NAME, ((EditText)findViewById(R.id.editText2)).getText().toString());
values.put(StudentsProvider.GRADE, ((EditText)findViewById(R.id.editText3)).getText().toString());
Uri uri = getContentResolver().insert(StudentsProvider.CONTENT_URI, values);
Toast.makeText(getBaseContext(), uri.toString(), Toast.LENGTH_LONG).show();
}

public void onClickRetrieveStudents(View view)
{
// Retrieve student records
String URL = “content://com.example.MyApplication.StudentsProvider”;
Uri students = Uri.parse(URL);
Cursor c = managedQuery(students, null, null, null, “name”);
if(c.moveToFirst())
{
do{
Toast.makeText(this, c.getString(c.getColumnIndex(StudentsProvider._ID)) + “, ” + c.getString(c.getColumnIndex( StudentsProvider.NAME)) + “, ” + c.getString(c.getColumnIndex( StudentsProvider.GRADE)), Toast.LENGTH_SHORT).show();
}while (c.moveToNext());
}
}
}

Step 3: Create a new java file under the same com.example.MyApplication package. Name this file as StudentsProvider.java. This file should be placed under the src/com.example.MyApplication path. Here, you will be defining your actual provider and the methods which are associated with this actual provider.

package com.example.MyApplication;
import java.util.HashMap;
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.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;

public class StudentsProvider extends ContentProvider
{
static final String PROVIDER_NAME = “com.example.MyApplication.StudentsProvider”;
static final String URL = “content://” + PROVIDER_NAME + “/students”;
static final Uri CONTENT_URI = Uri.parse(URL);
static final String _ID = “_id”;
static final String NAME = “name”;
static final String GRADE = “grade”;
private static HashMap<String, String> STUDENTS_PROJECTION_MAP;
static final int STUDENTS = 1;
static final int STUDENT_ID = 2;
static final UriMatcher uriMatcher;

static
{
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(PROVIDER_NAME, “students”, STUDENTS);
uriMatcher.addURI(PROVIDER_NAME, “students/#”, STUDENT_ID);
}

/**
* Database specific constant declarations
*/
private SQLiteDatabase db;
static final String DATABASE_NAME = “College”;
static final String STUDENTS_TABLE_NAME = “students”;
static final int DATABASE_VERSION = 1;
static final String CREATE_DB_TABLE = ” CREATE TABLE ” + STUDENTS_TABLE_NAME +
” (_id INTEGER PRIMARY KEY AUTOINCREMENT, ” +
” name TEXT NOT NULL, ” +
” grade TEXT 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 ” +  STUDENTS_TABLE_NAME);
onCreate(db);
}
}

@Override
public boolean onCreate()
{
Context context = getContext();
DatabaseHelper dbHelper = new DatabaseHelper(context);
db = dbHelper.getWritableDatabase();
return (db == null)? false:true;
}

@Override
public Uri insert(Uri uri, ContentValues values)
{
/**
* Add a new student record
*/
long rowID = db.insert( STUDENTS_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(STUDENTS_TABLE_NAME);
switch (uriMatcher.match(uri))
{
case STUDENTS:
qb.setProjectionMap(STUDENTS_PROJECTION_MAP);
break;
case STUDENT_ID: qb.appendWhere( _ID + “=” + uri.getPathSegments().get(1));
break;
default:
}

if (sortOrder == null || sortOrder == “”)
{
/**
* By default sort on student names
*/
sortOrder = NAME;
}

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 STUDENTS:
count = db.delete(STUDENTS_TABLE_NAME, selection, selectionArgs);
break;
case STUDENT_ID:
String id = uri.getPathSegments().get(1);
count = db.delete( STUDENTS_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 STUDENTS:
count = db.update(STUDENTS_TABLE_NAME, values, selection, selectionArgs);
break;
case STUDENT_ID:
count = db.update(STUDENTS_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 student records
*/
case STUDENTS:
return “vnd.android.cursor.dir/vnd.example.students”;

/**
* Get a particular student
*/
case STUDENT_ID:
return “vnd.android.cursor.item/vnd.example.students”;

default:
throw new IllegalArgumentException(“Unsupported URI: ” + uri);
}
}
}

Step 4: Open AndroidManifest.xml file. In this file, register your newly created content provider with the help of the <provider…/> tag.

<?xml version=”1.0″ encoding=”utf-8″?>
<manifest xmlns:android=”http://schemas.android.com/apk/res/android”
package=”com.example.MyApplication”>
<application
android:allowBackup=”true”
android:icon=”@mipmap/ic_launcher”
android:label=”@string/app_name”
android:supportsRtl=”true”
android:theme=”@style/AppTheme”>
<activity android:name=”.MainActivity”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
<provider android:name=”StudentsProvider”
android:authorities=”com.example.MyApplication.StudentsProvider”/>
</application>
</manifest>

Step 5: After this, you are required to make a few changes in the res/layout/activity_main.xml file. Here, you will create a GUI for adding the student records in the content provider.

<?xml version=”1.0″ encoding=”utf-8″?>
<RelativeLayout 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:paddingBottom=”@dimen/activity_vertical_margin”
android:paddingLeft=”@dimen/activity_horizontal_margin”
android:paddingRight=”@dimen/activity_horizontal_margin”
android:paddingTop=”@dimen/activity_vertical_margin”
tools:context=”com.example.MyApplication.MainActivity”>

<TextView
android:id=”@+id/textView1″
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”Content provider”
android:layout_alignParentTop=”true”
android:layout_centerHorizontal=”true”
android:textSize=”30dp” />

<TextView
android:id=”@+id/textView2″
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”W3School “
android:textColor=”#ff87ff09″
android:textSize=”30dp”
android:layout_below=”@+id/textView1″
android:layout_centerHorizontal=”true” />

<ImageButton
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:id=”@+id/imageButton”
android:src=”@drawable/abc”
android:layout_below=”@+id/textView2″
android:layout_centerHorizontal=”true” />

<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:id=”@+id/button2″
android:text=”Add Name”
android:layout_below=”@+id/editText3″
android:layout_alignRight=”@+id/textView2″
android:layout_alignEnd=”@+id/textView2″
android:layout_alignLeft=”@+id/textView2″
android:layout_alignStart=”@+id/textView2″
android:onClick=”onClickAddName”/>

<EditText
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:id=”@+id/editText”
android:layout_below=”@+id/imageButton”
android:layout_alignRight=”@+id/imageButton”
android:layout_alignEnd=”@+id/imageButton” />

<EditText
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:id=”@+id/editText2″
android:layout_alignTop=”@+id/editText”
android:layout_alignLeft=”@+id/textView1″
android:layout_alignStart=”@+id/textView1″
android:layout_alignRight=”@+id/textView1″
android:layout_alignEnd=”@+id/textView1″
android:hint=”Name”
android:textColorHint=”@android:color/holo_blue_light” />

<EditText
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:id=”@+id/editText3″
android:layout_below=”@+id/editText”
android:layout_alignLeft=”@+id/editText2″
android:layout_alignStart=”@+id/editText2″
android:layout_alignRight=”@+id/editText2″
android:layout_alignEnd=”@+id/editText2″
android:hint=”Grade”
android:textColorHint=”@android:color/holo_blue_bright” />

<Button
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”Retrive student”
android:id=”@+id/button”
android:layout_below=”@+id/button2″
android:layout_alignRight=”@+id/editText3″
android:layout_alignEnd=”@+id/editText3″
android:layout_alignLeft=”@+id/button2″
android:layout_alignStart=”@+id/button2″
android:onClick=”onClickRetrieveStudents”/>
</RelativeLayout>

Step 6: Leave the content of the string.xml file untouched. The Android Studio IDE takes care of this file on its own.

<?xml version=”1.0″ encoding=”utf-8″?>
<resources>
<string name=”app_name”>My Application</string>
</resources>;

Step 7: Run the Android application, after which, the Emulator window will be launched. Here, the result will be verified for all the changes done.

Well, for running the modified My Application, you need to have already created AVD. We are assuming that you are finished with the creation of AVD during the environment setup. Now, to run this application, open the Android Studio IDE. Now, open the any of the project activity files. After this, click on the run icon present in the toolbar of the software. The process will then be switched to the installation of the AVD in the Android Studio. And after the successful completion of the application, you will get the final display on the Emulator window.

Here, you will see two text fields asking for the Name and the Grade to be inserted in the database. Other than that, the application will give two options to the user; either to insert a record in the content provider or to retrieve the already existing record from the content provider. For this, the user gets the two options, which are: Add Name and Retrieve Student. For the first option of Add Name, the application will make use of insert() method. While for the second option of Retrieve Student, the application will be using query() method.

Similarly, you can add delete() method and update() method in the MainActivity.java file to perform the update and delete operations. Do not forget to add the appropriate buttons in the application by modifying the user interface present in the res/layout/activity_main.xml file.

In such a way, you can perform operations (like read, delete, update, and insert) on the already existing content provider.