android基础-contentProvider
2019-02-21 本文已影响0人
return_toLife
知识点
- uri类
- ContentProvider,ContentResolver
- 实现跨进程访问数据
一、Uri类
- uri定义:Uniform Resource Identifier,即统一资源标识符
- uri作用:外界进程通过 uri找到对应的contentProvider& 其中的数据,再进行数据操作
-
uri详解:
uri分为 系统预置 & 自定义,分别对应系统内置的数据(如通讯录、日程表等等)和自定义数据库
自定义的一般格式如下图:
图片来自网络.png
自定义uri的栗子
设置URI
Uri uri = Uri.parse("content://com.returnTolife.provider/test/1") ;
上述URI指向的资源是:名为 `com.returnTolife.provider`的`ContentProvider` 中表名 为`test` 中的 `id`为1的数据
特别注意:URI模式存在匹配通配符* & #
*:匹配任意长度的任何有效字符的字符串
以下的URI 表示 匹配provider的任何内容
"content://com.returnTolife.provider/* "
#:匹配任意长度的数字字符的字符串
以下的URI 表示 匹配provider中的test表的所有行
"content://com.returnTolife.provider/test/# "
二、ContentProvider和ContentResolver
- 关于ContentProvider:由于进程间共享数据的本质是:添加、删除、获取 & 修改(更新)数据,所以ContentProvider 其中的核心方法主要如下:
该方法在ContentProvider被创建后调用,当其他应用程序第一次访问ContentProvider时,该ContentProvider被创建。
public boolean onCreate():
根据Uri查询出selection条件所匹配的全部记录
public Cursor query():
该方法返回当前Uri所代表的数据的MIME类型。如果该Uri对应的数据可能包括多条记录,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,单条记录则为vnd.android.cursor.item/开头;
个人理解该方法返回的MIME在android主要是用来做页面跳转
public String getType()
根据该Uri插入values对应的数据。
public Uri insert():
根据Uri删除selection条件所匹配的全部记录。
public int delete():
根据Uri修改selection条件所匹配的全部记录
public int update():
1.1 关于getType()的作用可以参考ContentProvider数据库共享之——MIME类型与getType() ,文章中写得很详细,而且清晰易懂.
1.2 ContentProvider类并不会直接与外部进程交互,而是通过ContentResolver 类
-
关于ContentResolver :外部进程通过 ContentResolver类 从而与ContentProvider类进行交互,原因是:如果一款应用要使用多个ContentProvider,若需要了解每个ContentProvider的不同实现从而再完成数据交互,操作成本高并且难度会比较大所以再ContentProvider类上加多了一个 ContentResolver类对所有的ContentProvider进行统一管理。
使用栗子
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://com.returnTolife.provider/test/");
Cursor cursor = resolver.query(uri, null, null, null, null);
//其他方法类似,不重复
-
UriMatcher类:该类为contentProvider的辅助类,主要用于将一个uri地址与provider进行绑定
栗子
public static final int TABLE_STUDENT_CODE = 1;
public static final int TABLE_BOOKE_CODE = 2;
static{
//参数表示初始化时不匹配任何东西
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
//matcher .addURI的作用是在后面使用matcher.match(string s)匹配的时候,如果匹配到/student"时返回TABLE_STUDENT_CODE即数值1,匹配“/book”时返回MATCH_SECOND
matcher .addURI(AUTHORITY, "student", TABLE_STUDENT_CODE);
matcher .addURI(AUTHORITY, "book", TABLE_BOOKE_CODE)
}
三、实例
- 数据准备,先创建一个数据库
public class MyDbHelper extends SQLiteOpenHelper {
private static final String DB_NAME="myprovider.db";
public static final String TABLE_SUTEMT="student";
public static final String TABLE_BOOK="book";
private static final int VERSION=1;
public MyDbHelper(Context context) {
super(context, DB_NAME, null, VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_SUTEMT + "(_id INTEGER PRIMARY KEY AUTOINCREMENT," + " name TEXT)");
db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_BOOK + "(_id INTEGER PRIMARY KEY AUTOINCREMENT," + " name TEXT)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
- 新建一个MyContentProvider类,继承ContentProvider类
public class MyContentProvider extends ContentProvider {
private Context mContext;
private MyDbHelper mMyDbHelper;
// 设置ContentProvider的唯一标识
public static final String AUTOHORITY = "com.returntolife.myprovider";
public static final int TABLE_STUDENT_CODE = 1;
public static final int TABLE_BOOKE_CODE = 2;
// UriMatcher类使用:在ContentProvider 中注册URI
private static final UriMatcher mMatcher;
SQLiteDatabase db = null;
//重要的一步,将uri和provider绑定
static {
mMatcher=new UriMatcher(UriMatcher.NO_MATCH);
mMatcher.addURI(AUTOHORITY,MyDbHelper.TABLE_SUTEMT, TABLE_STUDENT_CODE);
mMatcher.addURI(AUTOHORITY, MyDbHelper.TABLE_BOOK, TABLE_BOOKE_CODE);
}
@Override
public boolean onCreate() {
mContext=getContext();
mMyDbHelper=new MyDbHelper(mContext);
db=mMyDbHelper.getWritableDatabase();
// 初始化两个表的数据(先清空两个表,再各加入一个记录)
db.execSQL("delete from student");
db.execSQL("insert into student values(1,'张三');");
db.execSQL("insert into student values(2,'李四');");
db.execSQL("delete from book");
db.execSQL("insert into book values(1,'Android');");
db.execSQL("insert into book values(2,'iOS');");
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
String table=getTableName(uri);
return db.query(table,null,null,null,null,null,null);
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
LogUtil.d("getType uri="+uri);
return "vnd.android.cursor.dir/harvic.first";
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
String table=getTableName(uri);
db.insert(table,null,values);
// 当该URI的ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
mContext.getContentResolver().notifyChange(uri, null);
return uri;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
/**
* 根据URI匹配 URI_CODE,从而匹配ContentProvider中相应的表名
*/
private String getTableName(Uri uri){
String tableName = null;
switch (mMatcher.match(uri)) {
case TABLE_STUDENT_CODE:
tableName = MyDbHelper.TABLE_SUTEMT;
break;
case TABLE_BOOKE_CODE:
tableName = MyDbHelper.TABLE_BOOK;
break;
default:
break;
}
return tableName;
}
}
- 在AndroidManifest.xml文件注册这个ContentProvider,绑定一个Uri
<provider
android:authorities="com.returntolife.myprovider"
android:name=".mycontentprovider.MyContentProvider"/>
- 自身内部访问直接通过ContentResolver就可以访问,不用添加什么权限
private void getData() {
ContentResolver contentResolver=getContentResolver();
Uri uri=Uri.parse("content://com.returntolife.myprovider/student");
Cursor cursor=contentResolver.query(uri,new String[]{"_id,name"},null,null,null);
if(cursor!=null){
while(cursor.moveToNext()){
LogUtil.d("id="+cursor.getInt(0)+"--name="+cursor.getString(1));
tvTest.append("id="+cursor.getInt(0)+"--name="+cursor.getString(1)+"\n");
}
cursor.close();
}
Uri uriBook=Uri.parse("content://com.returntolife.myprovider/book");
Cursor cursorBook=contentResolver.query(uriBook,new String[]{"_id,name"},null,null,null);
if(cursorBook!=null){
while(cursorBook.moveToNext()){
LogUtil.d("id="+cursorBook.getInt(0)+"--name="+cursorBook.getString(1));
}
cursorBook.close();
}
}
- 外部进程访问需要定义和添加权限
5.1 提供数据的进程(进程1)中自定义一个访问权限,并设置到对应的provider中,如下
<permission
android:name="com.returntolife.jjconde.providertestservice.provider"
android:protectionLevel="normal" />
<provider
android:name=".mycontentprovider.MyContentProvider"
android:authorities="com.returntolife.myprovider"
android:exported="true"
android:permission="com.returntolife.myprovider.permission" />
5.2 需要访问数据的进程(进程2)中添加上面定义的访问权限,然后其余获取数据基本和应用自身获取数据一致
<uses-permission android:name="com.returntolife.myprovider.permission"/>
// 设置URI
Uri uri=Uri.parse("content://com.returntolife.myprovider/student");
// 插入表中数据
// ContentValues values = new ContentValues();
// values.put("_id", 10);
// values.put("name", "Jordan");
// 获取ContentResolver
ContentResolver resolver = getContentResolver();
Cursor cursor = resolver.query(uri, new String[]{"_id","name"}, null, null, null);
if(cursor!=null){
while (cursor.moveToNext()){
Log.d("MainActivity","id="+cursor.getInt(0)+"--name="+cursor.getString(1)+"\n");
}
cursor.close();
}
总结
- contentProvider的难点主要是在理解uri的定义,以及如何将uri和provider绑定,访问方式其实不难
- contentProvider实现跨进程通信相对于AIDL的通信方式,会简洁方便很多
参考文章
Android:关于ContentProvider的知识都在这里了!
ContentProvider数据库共享之——MIME类型与getType()