ContentProvider主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序去访问另一个程序中的数据,同时还能保证被访数据的安全性。下面我们来总结一下。
内容提供器的用法一般有两种,一种是使用现有的内容提供器来读取和操作相应程序中的数据,另一种是创建自己的内容提供器给我们的程序的数据提供外部访问接口。
我们先来总结第一种,我们知道Android系统自带的电话簿,短信,媒体库等程序都提供了这样的访问接口,是的第三方可以访问这些数据,要想访问这些数据,我们需要用到ContentResolver类。
不同于SQLiteDatabase,ContentResolver中的增删改查方法都是不接收表名参数的,而是使用一个Uri参数代替,这个参数被称为内容URI。内容URI给内容提供器中的数据建立了唯一标识符,它主要由两部分组成:authority和path。authority是用于对不同的应用程序做区分的,一般为了避免冲突,都会用程序的包名的方式来进行命名,比如程序的包名是com.example.test,那么该程序对应的authority就可以命名为com.example.test.provider。path则是对同一程序中不用的表做区分,通常会添加到authority的后面。比如某个程序的数据库中有2张表table1和table2,那么path的命名则是/table1和/table2.然后把authority和path进行组合,内容URI就变成了com.example.test.provider/table1和com.example.test.provider/table2。因此内容URI最标准的格式写法如下
content://com.example.test.provider/table1
content://com.example.test.provider/table2
这样内容URI就可以非常清楚的表达我们想要访问哪个程序中的那张表中的数据。
下面我们写一段代码来读取手机联系人的信息。
try{
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);
if (cursor != null){
while(cursor.moveToNext()) {
String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.COmmonDataKinds.Phone.DISPLAY_NAME));
String displayNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.COmmonDataKinds.Phone.DISPLAY_NUMBER));
}catch(Exception e) {
e.printStackTrace()
}finally{
if (cursor!=null){
cursor.close();
}
}
千万不要忘记在AndroidManifest.xml中去申请权限
<uses-permission android:name="android.permission.READ_CONTACTS">
插入数据
ContentValue values = new ContentValue();
values.put("column1","text");
getContentResolver().insert(uri,values);
更新数据
ContentValues values = new ContentValues();
values.put("column1","");
getContentResolver().update(uri,values,"column1=?",new String[]{"text"});
删除数据
getContentResolver().delete(uri,"column1=?",new String[]{"1"});
好了到目前为止我们把ContentProvider的增删改查总结完了,下面我们总结一下如何创建自己的内容提供器。
回顾一下,一个标准的内容URI的写法:
content://com.example.test.provider/table1
其实我们还可以 在后面加上一个id
content://com.example.test.provider/table1/1
表示调用方期望访问的是com.example.test这个应用的table1表中id为1的数据。我们可以使用通配符的方式来分别匹配这两种格式的内容URI,规则如下
*:表示匹配任意长度的任意字符
#:表示匹配任意长度的数字
所以一个能够匹配任意表的内容URI格式就可以写成
content://com.example.test.provider/*
而能够匹配table1表中的任意一行数据的内容URI格式就写成
content://com.example.test.provider/table1/#;
第一步我们需要创建一个provider类去继承ContentProvider类。直接贴代码
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
public class MyProvider extends ContentProvider {
public static final int TABLE1_DIR = 0;
public static final int TABLE1_ITEM = 1;
public static final int TABLE2_DIR = 2;
public static final int TABLE2_ITEM = 3;
private static UriMatcher uriMatcher;
private MyDataBaseHelper dataBaseHelper;
//把期望匹配的内容URI格式传递进去,注意这里传入的路径参数可以使用通配符
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//对应的三个参数authority,path,和一个自定义代码
uriMatcher.addURI("com.example.test.provider","table1",TABLE1_DIR);
uriMatcher.addURI("com.example.test.provider","table1/#",TABLE1_ITEM);
uriMatcher.addURI("com.example.test.provider","table2",TABLE2_DIR);
uriMatcher.addURI("com.example.test.provider","table2/#",TABLE2_DIR);
}
public MyProvider() {
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = dataBaseHelper.getWriteableDatabase();
int deleteRows = 0;
switch (uriMatcher.match(uri)) {
case TABLE1_DIR:
deleteRows = db.delete("table1",selection,selectionArgs);
break;
case TABLE1_ITEM:
String id = uri.getPathSegments().get(1);
deleteRows = db.delete("table1","id=?",new String[]{id});
break;
case TABLE2_DIR:
deleteRows = db.delete("table2",selection,selectionArgs);
break;
case TABLE2_ITEM:
String id2 = uri.getPathSegments().get(1);
deleteRows = db.delete("table2","id=?",new String[]{id2});
break;
}
}
/**
* 用于获取Uri对象对应的MIME类型,一个内容URI所对应的MIME字符窜主要由3部分组成,
* Android对这3部分做了如下规定
* 1.必须以vnd开头
* 2.如果内容URI以路径结尾,则后接android.cursor.dir/,如果内容URI以id结尾,则后接android.cursor.item/
* 3.最后接上vnd.<authority>.<path>
* @param uri
* @return
*/
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case TABLE1_DIR:
return "vnd.android.cursor.dir/vnd.com.example.test.provider.table1";
case TABLE1_ITEM:
return "vnd.android.cursor.item/vnd.com.example.test.provider.table1";
case TABLE2_DIR:
return "vnd.android.cursor.dir/vnd.com.example.test.provider.table2";
case TABLE2_ITEM:
return "vnd.android.cursor.item/vnd.com.example.test.provider.table2";
}
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = dataBaseHelper.getWriteableDatabase();
Uri uriReturn = null;
switch (uriMatcher.match(uri)) {
case TABLE1_DIR:
case TABLE1_ITEM:
long newId = db.insert("table1",null,values);
uriReturn = Uri.parse("content://com.example.test.provide/table1" + newId);
break;
case TABLE2_DIR:
case TABLE2_ITEM:
long newId2 = db.insert("table2",null,values);
uriReturn = Uri.parse("content://com.example.test.provide/table2" + newId2);
break;
}
return uriReturn;
}
@Override
public boolean onCreate() {
// TODO: Implement this to initialize your content provider on startup.
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = dataBaseHelper.getReadableBase();
Cursor curs = null;
switch (uriMatcher.match(uri)) {
case TABLE1_DIR:
curs = db.query("table1",projection,selection,selectionArgs,null,null,sortOrder);
break;
case TABLE1_ITEM:
String id = uri.getPathSegments().get(1);
curs = db.query("table1",projection,"id=?",new String[]{id},null,null,sortOrder);
break;
case TABLE2_DIR:
curs = db.query("table2",projection,selection,selectionArgs,null,null,sortOrder);
break;
case TABLE2_ITEM:
String id2 = uri.getPathSegments().get(1);
curs = db.query("table2",projection,"id=?",new String[]{id2},null,null,sortOrder);
break;
}
return curs;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
SQLiteDatabase db = dataBaseHelper.getWriteableDatabase();
int updatedRows = 0;
switch (uriMatcher.match(uri)) {
case TABLE1_DIR:
updatedRows = db.update("table1",values,selection,selectionArgs);
break;
case TABLE1_ITEM:
String id = uri.getPathSegments().get(1);
updatedRows = db.update("table1",values,"id=?",new String[]{id});
break;
case TABLE2_DIR:
updatedRows = db.update("table2",values,selection,selectionArgs);
break;
case TABLE2_ITEM:
String id2 = uri.getPathSegments().get(1);
updatedRows = db.update("table2",values,"id=?",new String[]{id2});
break;
}
return updatedRows;
}
}
虽然代码多,但是很容易理解。
那么我们的另一个程序就可以通过ContentResolver类对我们的这个程序的数据库进行操作
我以更新数据为例写,其他的查找,插入,删除我就不写了,都是调用上面的方法即可
/**
* 更新某一行
*/
private void test() {
Uri uri = Uri.parse("content://com.example.test.provider/book/" + newId);
ContentValues values = new ContentValues();
values.put("name","A Storm of Swords");
getContentResolver().update(uri,values,null,null);
}