Android Content Providers
In this guide, we will study about a powerful Android component, Content Provider. Here, we will see:
- What are Content providers?
- Define Content URIs.
- Advantages of Content Providers.
- When to make and when not to make a Content Provider for your app.
- A demo application.
Content Provider
Content provider, as the name suggests, provides data from your app to other apps, on request. Such requests are handled by ContentResolver class. It can use different ways to store its data and the data can be stored in a database, in files, or on a Server.
>>> Content provider encapsulate the data, and provide mechanisms for defining data security.
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 DiscoverSdkApp extends ContentProvider {
}
Content providers let you centralize your data in one place and have many different applications access it upon request. They behave very much like a database where data can be queried, edited, added or deleted using insert(), update(), delete(), and query() methods. In most cases this data is stored in an SQlite database.
Content URIs
To query a content provider, we need to specify the query string in the form of a URI which is of the form:
<prefix>://<authority>/<data_type>/<id>
Here,
- prefix : This is always content://
- authority : This indicates the name of the content provider, for example browser, gallery etc. For our apps, this could be the fully qualified name, such as com.discoversdk.infoprovider.
- data_type : This indicates the type of data that this particular provider promises to provide.
- id : This indicates the specific resource.
Do you really need a Content Provider?
You need to build a provider if you want to provide one or more of the following functionalities:
- You want to offer complex data or files to other applications.
- You want to allow users to copy complex data from your app into other apps.
- You want to provide custom search suggestions using the search framework.
- You want to expose your application data to widgets.
>>> You don't need a provider to use databases or other types of persistent storage if the use is entirely within your own application and you don’t need any of the features listed above.
Create Content Provider
Follow below steps to define your own provider:
- Create a Content Provider class that extends the ContentProvider class.
- Next, define your content provider URI address which will be used to access the content.
- Next, create your app’s database to keep the content.
- Next we will have to implement Content Provider queries to perform different database specific operations.
- Finally register the app’s Content Provider in the activity file using <provider> tag.
We will be creating an example app which will clearly demonstrate each mentioned step.
Retrieving Data from the Provider
To retrieve data from a provider, follow these basic steps:
- Request the read access permission for the provider.
- Define the code that sends a query to the provider.
SQLiteOpenHelper vs Content Provider
SQLiteOpenHelper is used to manage database creation and open. It is only a helper class for sqlite database and could be used in anywhere with db operation.
ContentProvider is one of the 4 basic components of android, the other 3 are Activity, Service and Broadcast. ContentProvider is used to manage access to any structured data set, including sqlite database. The data source of ContentProvider could be a database but not have to be.
ContentProvider is often used in providing own data to others applications, just like ContactsProvider and CalendarProvider. And it is more safe to provide some specified interface using ContentProvider, compared with providing a database directly.
>>> Difference between a Content provider and a Content resolver is such that Content Resolver resolves a URI to a specific Content provider whereas a Content Provider provides an interface to query content.
SharedPreferences vs Content Provider
Shared preferences are the location where we can store the secret information for our app, like setting cookies in the browser, this can be used for login credentials and other.
For content provider, it stores and retrieves the data and make it available to other applications also. Suppose you want to access the contacts available in the android phone, they can be accessed by content providers.
Example
Now, let us have a look at an example of how to create a Content provider sample application. We will use Android Studio IDE to create an Android application.
Following is the content of the modified main activity file src/com.discoversdk.MyApplication/MainActivity.java. This file will include each of the fundamental life cycle methods. We have added two new methods onClickAddName() and onClickRetrieveStudents() to handle user interaction with the application.
package com.discoversdk.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.discoversdk.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());
}
}
}
Let’s create a new file StudentsProvider.java under com.discoversdk.MyApplication package and put below code in the file src/com.discoversdk.MyApplication/StudentsProvider.java:
package com.discoversdk.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.discoversdk.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);
/**
* 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 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.discoversdk.students";
/**
* Get a particular student
*/
case STUDENT_ID:
return "vnd.android.cursor.item/vnd.discoversdk.students";
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
}
}
Look at the AndroidManifest.xml file. We need to add <provider.../> tag to include the created content provider:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.discoversdk.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.discoversdk.MyApplication.StudentsProvider"/>
</application>
</manifest>
Our layout file in res/layout/activity_main.xml will look like
<?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="DiscoverSDK "
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>
Make sure you have the app name in res/values/strings.xml file:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Discover SDK App</string>
</resources>
Let's try to run our modified My Application application we just created.
This way you can use existing Content Provider like Address Book or you can use Content Provider concept in developing nice database oriented applications where you can perform all sort of database operations like read, write, update and delete as explained above in the sample application.
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment