On the Android platform, one application cannot directly access (read/write) other application's data. All persistent data of an app is private to that app. Every application has its own id data directory and own protected memory area. This means that an app cannot query or manipulate the data of another app. However, if you want to enable an app to query or manipulate the data of another app, you need to use the concept of content providers.
Content Provider:
Content provider is a set of data wrapped up in a custom API to read and write. It acts as an interface that allows you to store and retrieve data from a data source. And also allows you to share an app’s data with other apps. Content providers decouple the app layer from the data layer by abstracting the underlying data source, thereby making apps data-source independent. They allow full permission control by monitoring which app components or users should have access to the data making data sharing easy. As a result, any app with appropriate permissions can add, remove, update, and retrieve data of another app including the data in some native Android databases.
There are 2 types of Content Providers :
- Custom content providers: These are created by the developer to suit the requirements of an app.
- Native content providers: These provide access to built-in databases, such as contact manager, media player, and other native databases. You need to grant the required permissions to your app before using native content providers.
Eg : The WhatsApp app sharing mobile contacts data.
Media store-Allows other applications to access, store media files.
Above diagram shows how content provider works. Application 2 stores its data in its own database and provides a provider. Application 1 communicates with the provider to access Application 2's data.
Content Provider uses a special URI starting with content:// will be assigned to each content providers and that will be recognized across applications.
Creating a Content Provider :
To create a content provider we need to
- Create sub class for ContentProvider.
- Define content URI
- Implement all the unimplemented methods. insert(), update(), query(), delete(), getType().
- Declare the content provider in AndroidManifest.xml
Specifying the URI of a Content Provide
Content provider URI consists of four parts.
content://authority/path/id
content://authority/path/id
Each content provider exposes a URI that uniquely identifies the content provider’s data set. If a content provider controls multiple datasets, a separate URI needs to be specified for each dataset. The URI of a content provider begins with the string, content://.
content:// - All the content provider URIs should start with this value
authority - A Java namespace of the content provider implementation. (fully qualified Java package name)
path - A virtual directory within the provider that identifies the kind of data being requested.
Id - Its optional part that specifies the primary key of a record being requested. We can omit this part to request all records.
Registering the provider in AndroidManifest.xml
As with any other major parts of Android application, we have to register the content providers too in the AndroidManifest.xml. <provider> element is used to register the content providers. It should be declared under <application>.
<provider
android:name=".MyProvider"
android:authorities="com.example.contentproviderexample.MyProvider"
android:exported="true"
android:multiprocess="true" >
</provider>
Here authorities is the URI authority to access the content provider. Typically this will be the fully qualified name of the content provider class.Creating provider application:
This application will create a content provider and share its data. This uses SQLite database 'sharedDb'. It has a table called 'names'. The table names have two columns id, name.
The URI of this content provider is content://com.example.contentproviderexample.MyProvider/cte
We have layout to insert new records to the database table.
We have layout to insert new records to the database table.
Step 1 : Create a new project by going to File ⇒ New Android Application Project. Fill all the details and name your activity as MainActivity. (eclipse)
Step 2 : Design the activity_main.xml layout for MainActivity under layout folder as shown below for capturing data.
activity_main.xml
<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"
tools:context = "${relativePackage}.${activityClass}" >
<TextView
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:text = "Enter Name :"
android:textAppearance = "?android:attr/textAppearanceSmall"/>
<EditText
android:id = "@+id/txtName"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"/>
<Button
android:id = "@+id/btnAdd"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:onClick = "onClickAddName"
android:text = "Add Name"/>
</LinearLayout >
Step 3 : Creating a Custom Content Provider
To create your own content provider, you need to create a class that extends the abstract ContentProvider class and overrides its methods.
MyProvider.java
public class MyProvider extends ContentProvider
{
static final String PROVIDER_NAME = "com.example.contentproviderexample.MyProvider";
static final String URL = "content://" + PROVIDER_NAME + "/cte";
Step 5 : Create a new project by going to File ⇒ New Android Application Project. Name it as 'RequestApp' Fill all the details and name your activity as MainActivity. (eclipse)
static final Uri CONTENT_URI = Uri.parse(URL);
static final String id = "id";
static final String name = "name";
static final String uriCode = 1;
static final String UriMatcher uriMatcher;
private static HashMap<String,String> values;
static
{
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(PROVIDER_NAME ,"cte", uriCode);
uriMatcher.addURI(PROVIDER_NAME ,"cte/*", uriCode);
}
static final String id = "id";
static final String name = "name";
static final String uriCode = 1;
static final String UriMatcher uriMatcher;
private static HashMap<String,String> values;
static
{
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(PROVIDER_NAME ,"cte", uriCode);
uriMatcher.addURI(PROVIDER_NAME ,"cte/*", uriCode);
}
private SQLiteDatabase db;
static final String DATABASE_NAME = "sharedDb";
static final String TABLE_NAME = "names";
static final int DATABASE_VERSION = 1;
static final String CREATE_DB_TABLE = " CREATE TABLE " + TABLE_NAME
+ " (id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ " name TEXT NOT NULL);";
static final String DATABASE_NAME = "sharedDb";
static final String TABLE_NAME = "names";
static final int DATABASE_VERSION = 1;
static final String CREATE_DB_TABLE = " CREATE TABLE " + TABLE_NAME
+ " (id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ " name TEXT NOT NULL);";
@Override
public boolean onCreate()
{
Context context = getContext();
DatabaseHelper dbHelper = new DatabaseHelper(context);
db = dbHelper.getWritableDatabase();
if (db != null) {
return true;
}
return false;
}
@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 uriCode:
qb.setProjectionMap(values);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (sortOrder == null || sortOrder == "") {
sortOrder = name;
}
Cursor c = qb.query(db, projection, selection, selectionArgs, null,
null, sortOrder);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public String getType(Uri uri)
{
switch (uriMatcher.match(uri)) {
case uriCode :
return "vnd.android.cursor.dir/cte";
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values)
{
long rowID = db.insert(TABLE_NAME, "", values);
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 int delete(Uri uri, String selection, String[] selectionArgs)
{
int count = 0;
switch (uriMatcher.match(uri)) {
case uriCode :
count = db.delete(TABLE_NAME, 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 uriCode :
count = db.update(TABLE_NAME, values, selection, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
private static class DatabaseHelper extends SQLiteOpenHelper
{
{
DatabaseHelper(Context context)
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db)
{
db.execSQL(CREATE_DB_TABLE);
}
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);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}
}
Step 4 : Now open up the MainActivity from src/package. Add the following code.
Note : onClickAddName() is the onClick attribute declared inside layout.
public void onClickAddName(View view)
{
ContentValues values = new ContentValues();
values.put(MyProvider.name, ((EditText) findViewById(R.id.txtName))
.getText().toString());
Uri uri = getContentResolver().insert(MyProvider.CONTENT_URI, values);
((EditText) findViewById(R.id.txtName)).setText("");
Toast.makeText(getBaseContext(), "New record inserted", Toast.LENGTH_LONG)
.show();
ContentValues values = new ContentValues();
values.put(MyProvider.name, ((EditText) findViewById(R.id.txtName))
.getText().toString());
Uri uri = getContentResolver().insert(MyProvider.CONTENT_URI, values);
((EditText) findViewById(R.id.txtName)).setText("");
Toast.makeText(getBaseContext(), "New record inserted", Toast.LENGTH_LONG)
.show();
}
Creating request application:
This application is very simple, it will use the query() method to access the data stored in the Provider application. This application is having a layout to display the data.
Step 6 : Design the activity_main.xml layout for MainActivity under layout folder as shown below for displaying data.
activity_main.xml
<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"
tools:context = "${relativePackage}.${activityClass}" >
<Button
android:id = "@+id/btnRetrieve"
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:onClick = "onClickDisplayNames"
android:text = "Display Names"/>
<TextView
android:layout_width = "wrap_content"
android:id = "@+id/res"
android:layout_height = "wrap_content"
android:textAppearance = "?android:attr/textAppearanceMedium"/>
</LinearLayout >
Step 7 : Now open up the MainActivity from src/package. Add the following code.We have used CusrsorLoader to load the data.
MainActivity.java
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.content.CursorLoader;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.content.CursorLoader;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity implements LoaderManager.LoaderCallbacks<Cursor>
{
TextView resultView = null;
CursorLoader cursorLoader;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
resultView= (TextView) findViewById(R.id.res);
}
@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1)
{
cursorLoader = new CursorLoader(this, Uri.parse("content://com.example.contentproviderexample.MyProvider/cte"), null, null, null, null);
return cursorLoader;
}
public void onClickDisplayNames(View view)
{
getSupportLoaderManager().initLoader(1, null, this);
}
@Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor)
{
if (cursor != null)
{
cursor.moveToFirst();
StringBuilder res = new StringBuilder();
while (!cursor.isAfterLast()) {
res.append("\n"+cursor.getString(cursor.getColumnIndex("id"))+ "-"+ cursor.getString(cursor.getColumnIndex("name")));
cursor.moveToNext();
}
resultView.setText(res);
}
else
{
Toast.makeText(getBaseContext(), "No records", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onLoaderReset(Loader<Cursor> arg0)
{
}
CursorLoader cursorLoader;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
resultView= (TextView) findViewById(R.id.res);
}
@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1)
{
cursorLoader = new CursorLoader(this, Uri.parse("content://com.example.contentproviderexample.MyProvider/cte"), null, null, null, null);
return cursorLoader;
}
public void onClickDisplayNames(View view)
{
getSupportLoaderManager().initLoader(1, null, this);
}
@Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor)
{
if (cursor != null)
{
cursor.moveToFirst();
StringBuilder res = new StringBuilder();
while (!cursor.isAfterLast()) {
res.append("\n"+cursor.getString(cursor.getColumnIndex("id"))+ "-"+ cursor.getString(cursor.getColumnIndex("name")));
cursor.moveToNext();
}
resultView.setText(res);
}
else
{
Toast.makeText(getBaseContext(), "No records", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onLoaderReset(Loader<Cursor> arg0)
{
}
}
Source code : Click here to download.
Source code : Click here to download.
Thanks a lot for reading...
Don't forget to share this post if you found it interesting!
If you find something to add to this post? or any other quick thoughts/hints that you think people will find useful? Share it in the comments & feedback's are most welcome.