ContentProvider 简介
用于在不同的应用程序之间实现数据共享的功能。ContentProvider 可以理解为应用对外开放的接口,只要是符合它所定义的 Uri 格式的请求,均可以正常访问执行操作。其他的应用可以使用 ContentResolver 对象通过与 ContentProvider 同名的方法请求执行,被执行的就是 ContentProvider 中的同名方法。
ContentProvider的方法
boolean onCreate()
初始化提供者
Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder)
查询数据,返回一个数据Cursor对象
Uri insert(Uri uri, ContentValues values)
插入一条数据。参数values是需要插入的值
int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
根据条件更新数据。其中参数selection和selectionArgs是外部程序提供的条件
int delete(Uri uri, String selection, String[] selectionArgs)
根据条件删除数据
String getType(Uri uri)
返回MIME类型对应内容的URI
备注:还有一个 call() 方法,使用该方法,理论上可以在 ContentResolver 中执行 ContentProvider 暴露出来的任何方法。
Uri
在 Android 中,Uri 是一种比较常见的资源访问方式。而对于 ContentProvider 而言,Uri 也是有固定格式的:
<srandard_prefix>://<authority>/<data_path>/<id>
srandard_prefix:ContentProvider 的 srandard_prefix 始终是 content:。
authority:ContentProvider 的名称。
data_path:请求的数据类型。
id:指定请求的特定数据。
在 ContentProvider 的 CRUD 操作,均会传递一个 Uri 对象,通过这个对象来匹配对应的请求。那么如何确定一个 Uri 执行哪项操作呢?需要用到一个 UriMatcher 对象,这个对象用来帮助内容提供者匹配 Uri 。它所提供的方法非常简单,仅有两个:
添加一个 Uri 匹配项
void addURI(String authority,String path,int code)
- authority : 为 AndroidManifest.xml 中注册的 ContentProvider 中的 authority 属性;
- path 为一个路径,可以设置通配符,#表示任意数字,*表示任意字符;
- code 为自定义的一个 Uri 代码。
匹配传递的 Uri ,返回 addURI() 传递的 code 参数
int match(Uri uri):
代码示例:
总共有4个类,DBHelper(用于初始化SQLite数据库),PersonDao(用于CRUD操作的工具类),PersonProvider(用于对外开放的接口),ProviderTest(用于测试)
DBHelper
public class DBHelper extends SQLiteOpenHelper {
private static String name = "test.db"; //数据库名字
private static int version = 1; //数据库版本
public DBHelper( Context context) {
super(context, name, null, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
// 只能支持基本数据类型 : varchar int long float boolean text blob
String sql = "create table person(id integer primary key autoincrement,name varchar(64),address varchar(64))";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
String sql = "alter table person add sex varcher(8)";
db.execSQL(sql);
}
}
PersonDao
public class PersonDao {
private DBHelper mDBHelper;
public PersonDao(Context context) {
mDBHelper = new DBHelper(context);
}
public long insertPserson(ContentValues values) {
//返回插入的行号
long id = -1;
SQLiteDatabase database = null;
try {
database = mDBHelper.getWritableDatabase();
id = database.insert("person", null, values);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (database != null) {
database.close();
}
}
return id;
}
public int deletePerson(String whereClause, String[] whereArgs) {
//返回删除的条数
int count = -1;
SQLiteDatabase database = null;
try {
database = mDBHelper.getWritableDatabase();
count = database.delete("person", whereClause, whereArgs);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (database != null) {
database.close();
}
}
return count;
}
public int updatePerson(ContentValues values, String whereClause, String[] whereArgs) {
//返回更新的条数
int count = -1;
SQLiteDatabase database = null;
try {
database = mDBHelper.getWritableDatabase();
count = database.update("person",values, whereClause, whereArgs);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (database != null) {
database.close();
}
}
return count;
}
public Cursor queryPersons(String selection, String[] selectionArgs) {
SQLiteDatabase database = null;
Cursor cursor = null;
try {
// 这里获取读数据库
database = mDBHelper.getReadableDatabase();
cursor = database.query(true, "person", null, selection, selectionArgs, null, null, null, null);
} catch (Exception e) {
e.printStackTrace();
} finally {
}
return cursor;
}
}
PersonProvider
public class PersonProvider extends ContentProvider {
private final String TAG = "PersonProvider: ";
private PersonDao mPersonDao;
private final static String AUTHORITY = "zeng.fanda.com.fourmoduledemo.provider.PersonProvider";
private final static UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); //默认规则不匹配
// 操作单行记录
private static final int PERSON = 1;
// 操作多行记录
private static final int PERSONS = 2;
static {
//添加两个匹配规则
URI_MATCHER.addURI(AUTHORITY, "person", PERSONS);
//使用通配符#匹配任意数字
URI_MATCHER.addURI(AUTHORITY, "person/#", PERSON);
}
@Override
public boolean onCreate() {
//初始化实例
mPersonDao = new PersonDao(getContext());
Logger.i(TAG + "onCreate");
return true;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Cursor cursor = null;
//匹配uri ,返回自定义code
int flag = URI_MATCHER.match(uri);
switch (flag) {
case PERSON:
// 解析出uri中结尾的id
long id = ContentUris.parseId(uri);
cursor = mPersonDao.queryPersons("id = ?", new String[]{String.valueOf(id)});
break;
case PERSONS:
cursor = mPersonDao.queryPersons(selection, selectionArgs);
break;
}
if (cursor != null) {
Logger.i(TAG + "--》查询成功,count = " + cursor.getCount());
}
return cursor;
}
@Nullable
@Override
public String getType(Uri uri) {
// 用于获取uri对象所对应的MIME类型
int flag = URI_MATCHER.match(uri);
switch (flag) {
case PERSON:
//单条记录,规则为 vnd.android.cursor.item/自定义path
return "vnd.android.cursor.item/person";
case PERSONS:
//多条记录,规则为 vnd.android.cursor.dir/自定义path
return "vnd.android.cursor.dir/persons";
}
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, @Nullable ContentValues values) {
Uri resultUri = null;
// 解析Uri ,返回Code
int flag = URI_MATCHER.match(uri);
switch (flag) {
case PERSONS:
// 执行插入操作,返回当前的行号
long id = mPersonDao.insertPserson(values);
resultUri = ContentUris.withAppendedId(uri, id);
Logger.i(TAG + "--》插入成功,id = " + id);
Logger.i(TAG + "--》插入成功,resultUri = " + resultUri.toString());
}
return resultUri;
}
@Override
public int delete(Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
int count = -1; //影响数据库的行数
int flag = URI_MATCHER.match(uri);
switch (flag) {
case PERSON:
// 单条数据,使用 ContentUris 工具类解析出结尾的 Id
long id = ContentUris.parseId(uri);
count = mPersonDao.deletePerson("id = ?", new String[]{String.valueOf(id)});
break;
case PERSONS:
count = mPersonDao.deletePerson(selection, selectionArgs);
break;
}
Logger.i(TAG + "--》删除成功,count = " + count);
return count;
}
@Override
public int update(Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
int count = -1;
int flag = URI_MATCHER.match(uri);
switch (flag) {
case PERSON:
long id = ContentUris.parseId(uri);
count = mPersonDao.updatePerson(values, "id = ?", new String[]{String.valueOf(id)});
break;
case PERSONS:
count = mPersonDao.updatePerson(values, selection, selectionArgs);
break;
}
Logger.i(TAG + "--》更新成功,count = " + count);
return count;
}
@Nullable
@Override
public Bundle call( @NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
Bundle bundle = new Bundle();
bundle.putString("returnCall","call被执行了");
return bundle;
}
}
ProviderTest
@RunWith(AndroidJUnit4.class)
public class ProviderTest {
@Test
public void callTest() {
Context appContext = InstrumentationRegistry.getTargetContext();
ContentResolver resolver = appContext.getContentResolver();
Uri uri = Uri.parse("content://zeng.fanda.com.fourmoduledemo.provider.PersonProvider/person");
Bundle bundle = resolver.call(uri, "method", null, null);
Logger.d("callTest: " + bundle.getString("returnCall"));
}
@Test
public void insertTest() {
// 插入数据
Context appContext = InstrumentationRegistry.getTargetContext();
ContentResolver resolver = appContext.getContentResolver();
Uri uri = Uri.parse("content://zeng.fanda.com.fourmoduledemo.provider.PersonProvider/person");
ContentValues values = new ContentValues();
values.put("name", "蛋卷");
values.put("address", "茶光村");
resolver.insert(uri, values);
}
@Test
public void deleteTest() {
// 根据 id 来删除
Context appContext = InstrumentationRegistry.getTargetContext();
ContentResolver resolver = appContext.getContentResolver();
Uri uri = Uri.parse("content://zeng.fanda.com.fourmoduledemo.provider.PersonProvider/person/2");
resolver.delete(uri, null, null);
}
@Test
public void updateTest() {
// 根据 id 来更新
Context appContext = InstrumentationRegistry.getTargetContext();
ContentResolver resolver = appContext.getContentResolver();
Uri uri = Uri.parse("content://zeng.fanda.com.fourmoduledemo.provider.PersonProvider/person/1");
ContentValues values = new ContentValues();
values.put("name", "乖巧");
values.put("address", "珠光村");
resolver.update(uri, values,null, null);
}
@Test
public void queryTest() {
//查询所有
Context appContext = InstrumentationRegistry.getTargetContext();
ContentResolver resolver = appContext.getContentResolver();
Uri uri = Uri.parse("content://zeng.fanda.com.fourmoduledemo.provider.PersonProvider/person");
Cursor cursor = resolver.query(uri, null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
Logger.d("PersonProvider: " + cursor.getString(cursor.getColumnIndex("name")));
}
cursor.close();
}
}
@Test
public void querySelectionTest() {
// 根据查询条件测试
Context appContext = InstrumentationRegistry.getTargetContext();
ContentResolver resolver = appContext.getContentResolver();
Uri uri = Uri.parse("content://zeng.fanda.com.fourmoduledemo.provider.PersonProvider/person/3");
Cursor cursor = resolver.query(uri, null, "name=?", new String[]{"fanda"}, null);
if (cursor != null) {
while (cursor.moveToNext()) {
Logger.d("PersonProvider: " + cursor.getString(cursor.getColumnIndex("name")));
}
}
}
}
此外,要在清单文件 AndroidManifest 中注册:
<provider
android:name=".provider.PersonProvider"
android:authorities="zeng.fanda.com.fourmoduledemo.provider.PersonProvider"
android:exported="true" />
注意:exported = true 时,才可能跨应用跨进程访问数据。
下面是分别调用单元测试的方法所打印的日志:
insertTest方法:
deleteTest 方法:
updateTest 方法:
queryTest 方法:
callTest 方法: