ContentProvider 的作用
Android 提供的用于不同应用间进行数据共享的方式 contentprovider 内部使用binder 原理进行通信。
采用的数据源是Android
中的SQLite
数据库 ,文件 ,xml 数据 ,网络数据等
目录
(2) 访问 在这一步 就可以在的 activity 中去访问了 ,比如查询的方法(query)
(3)创建SQLiteOpenHelper (数据库帮助类)
辅助类介绍 URI ,UriMatcher ,ContentReslover:
统一资源标识符(URI)
定义:Uniform Resource Identifier,即统一资源标识符
作用:唯一标识 ContentProvider & 其中的数据
外界进程通过 URI 找到对应的ContentProvider & 其中的数据,再进行数据操作
具体使用
URI分为 系统预置 & 自定义,分别对应系统内置的数据(如通讯录、日程表等等)和自定义数据库
1. 关于 系统预置URI 此处不作过多讲解,需要的同学可自行查看
2. 此处主要讲解 自定义URI
/ 设置URI
Uri uri = Uri.parse("content://com.carson.provider/User/1")
// 上述URI指向的资源是:名为 `com.carson.provider`的`ContentProvider` 中表名 为`User` 中的 `id`为1的数据
// 特别注意:URI模式存在匹配通配符* & #
// *:匹配任意长度的任何有效字符的字符串
// 以下的URI 表示 匹配provider的任何内容
content://com.example.app.provider/*
// #:匹配任意长度的数字字符的字符串
// 以下的URI 表示 匹配provider中的table表的所有行
content://com.example.app.provider/table/#
ContentUris
UriMatcher
ContentObserver
ContentUris类
作用:操作 URI
具体使用
核心方法有两个:withAppendedId() &parseId()
// withAppendedId()作用:向URI追加一个id
Uri uri = Uri.parse("content://cn.scu.myprovider/user")
Uri resultUri = ContentUris.withAppendedId(uri, 7);
// 最终生成后的Uri为:content://cn.scu.myprovider/user/7
// parseId()作用:从URL中获取ID
Uri uri = Uri.parse("content://cn.scu.myprovider/user/7")
long personid = ContentUris.parseId(uri);
//获取的结果为:7
---------------------
UriMatcher类
作用
在ContentProvider 中注册URI
根据 URI 匹配 ContentProvider 中对应的数据表
具体使用
// 步骤1:初始化UriMatcher对象
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
//常量UriMatcher.NO_MATCH = 不匹配任何路径的返回码
// 即初始化时不匹配任何东西
// 步骤2:在ContentProvider 中注册URI(addURI())
int URI_CODE_a = 1;
int URI_CODE_b = 2;
matcher.addURI("cn.scu.myprovider", "user1", URI_CODE_a);
matcher.addURI("cn.scu.myprovider", "user2", URI_CODE_b);
// 若URI资源路径 = content://cn.scu.myprovider/user1 ,则返回注册码URI_CODE_a
// 若URI资源路径 = content://cn.scu.myprovider/user2 ,则返回注册码URI_CODE_b
// 步骤3:根据URI 匹配 URI_CODE,从而匹配ContentProvider中相应的资源(match())
@Override
public String getType(Uri uri) {
Uri uri = Uri.parse(" content://cn.scu.myprovider/user1");
switch(matcher.match(uri)){
// 根据URI匹配的返回码是URI_CODE_a
// 即matcher.match(uri) == URI_CODE_a
case URI_CODE_a:
return tableNameUser1;
// 如果根据URI匹配的返回码是URI_CODE_a,则返回ContentProvider中的名为tableNameUser1的表
case URI_CODE_b:
return tableNameUser2;
// 如果根据URI匹配的返回码是URI_CODE_b,则返回ContentProvider中的名为tableNameUser2的表
}
}
ContentObserver类
定义:内容观察者
作用:观察 Uri引起 ContentProvider 中的数据变化 & 通知外界(即访问该数据访问者)
当ContentProvider 中的数据发生变化(增、删 & 改)时,就会触发该 ContentObserver类
// 步骤1:注册内容观察者ContentObserver
getContentResolver().registerContentObserver(uri);
// 通过ContentResolver类进行注册,并指定需要观察的URI
// 步骤2:当该URI的ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
public class UserContentProvider extends ContentProvider {
public Uri insert(Uri uri, ContentValues values) {
db.insert("user", "userid", values);
getContext().getContentResolver().notifyChange(uri, null);
// 通知访问者
}
}
// 步骤3:解除观察者
getContentResolver().unregisterContentObserver(uri);
// 同样需要通过ContentResolver类进行解
---------------------
4.2 MIME数据类型
作用:指定某个扩展名的文件用某种应用程序来打开
如指定.html文件采用text应用程序打开、指定.pdf文件采用flash应用程序打开
具体使用:
4.2.1 ContentProvider根据 URI 返回MIME类型
ContentProvider.geType(uri) ;
1
4.2.2 MIME类型组成
每种MIME类型 由2部分组成 = 类型 + 子类型
MIME类型是 一个 包含2部分的字符串
text / html
// 类型 = text、子类型 = html
text/css
text/xml
application/pdf
1
2
3
4
5
6
4.2.3 MIME类型形式
MIME类型有2种形式:
// 形式1:单条记录
vnd.android.cursor.item/自定义
// 形式2:多条记录(集合)
vnd.android.cursor.dir/自定义
// 注:
// 1. vnd:表示父类型和子类型具有非标准的、特定的形式。
// 2. 父类型已固定好(即不能更改),只能区别是单条还是多条记录
// 3. 子类型可自定义
---------------------
使用 需要 三步:
(1) 创建一个 类 继承 ContentProvider ,在清单文件中注册(四大组件都需要注册)并声明authorities 。
并且重写 六个抽象方法 oncreate ,query ,update ,insert ,delete gettype;
oncreate :contentprovider 的创建方法 用来做初始化的操作 。
query :查询方法 。
update 修改(更新)数据方法
insert 添加数据方法
delete 删除数据 。
gettype 返回uri 请求所对应的MiME 类型 ,图片和视频 数据的类型 。
android :authorities ="com.example.liq.apfirst.Provider.BookProvider "; 这个是自定义的 ,可以是 你应用的包名
android:exported="true" // 设置此provider是否可以被其他进程使用
// 声明本应用 可允许通信的权限 <permission android:name="scut.carson_ho.Read" android:protectionLevel="normal"/>
// 声明外界进程可访问该Provider的权限(读 & 写)
android:permission="scut.carson_ho.PROVIDER"
// 权限可细分为读 & 写的权限
// 外界需要声明同样的读 & 写的权限才可进行相应操作,否则会报错
// android:readPermisson = "scut.carson_ho.Read"
// android:writePermisson = "scut.carson_ho.Write"
---------------------
补充 一点 :
根据binder的工作原理 ,除了oncreate 方法 由系统回调并运行在主线程,其他五个方法 都运行在contentprovider 的进程中 由外界回调 并运行在Binder 的线程池
@Override public boolean onCreate() { mContext = getContext(); initProviderData(); return true;; } private void initProviderData() { mDb = new DbOpenHelper(mContext).getWritableDatabase(); mDb.execSQL("delete from " + DbOpenHelper.BOOK_TABLE_NAME); mDb.execSQL("delete from " + DbOpenHelper.USER_TALBE_NAME); mDb.execSQL("insert into book values(3,'Android');"); mDb.execSQL("insert into book values(4,'Ios');"); mDb.execSQL("insert into book values(5,'Html5');"); mDb.execSQL("insert into user values(1,'jake',1);"); mDb.execSQL("insert into user values(2,'jasmine',0);"); } @Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { String table=getTableName(uri); if (table==null){ throw new IllegalArgumentException("Unsuppported uri"); } return mdb.query(table,projection,selection,selectionArgs,null,null,sortOrder,null);
} @Nullable @Override public String getType(@NonNull Uri uri) { return null; } @Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { String table=getTableName(uri); if (table==null){ throw new IllegalArgumentException("unsportted uri"+uri); } mdb.insert(table,null,values); mcontext.getContentResolver().notifyChange(uri,null); return uri; } @Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { String table=getTableName(uri); if (table==null){ throw new IllegalArgumentException("unsportted uri"+uri); } int count=mdb.delete(table,selection,selectionArgs); if (count>0){ getContext().getContentResolver().notifyChange(uri,null); } return count; } @Override public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { String table=getTableName(uri); if (table==null){ throw new IllegalArgumentException("unsupported URI"+uri); } int row=mdb.update(table,values,selection,selectionArgs); if (row>0){ getContext().getContentResolver().notifyChange(uri,null); } return row;
}
(2) 访问 在这一步 就可以在的 activity 中去访问了 ,比如查询的方法(query)
Uri uri=Uri.parse("content://com.com.example.liq.apfirst.Provider.BookProvider/user"); //括号内的参数 就是在清单文件注册的 authorities+数据库表名 // 设置ContentProvider的URI
ContentResolver contentResolver=getContentResolver();// 可通过在所有继承Context的类中 通过调用getContentResolver()来获 得ContentResolver
Uri bookUri = Uri.parse("content://com.ryg.chapter_2.book.provider/book");
ContentValues values = new ContentValues();
values.put("_id", 6);
values.put("name", "程序设计的艺术");
getContentResolver().insert(bookUri, values);
Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "name"}, null, null, null);
while (bookCursor.moveToNext()) {
Book book = new Book();
book.bookId = bookCursor.getInt(0);
book.bookName = bookCursor.getString(1);
Log.d(TAG, "query book:" + book.toString());
}
bookCursor.close();
Uri userUri = Uri.parse("content://com.ryg.chapter_2.book.provider/user");
Cursor userCursor = getContentResolver().query(userUri, new String[]{"_id", "name", "sex"}, null, null, null);
while (userCursor.moveToNext()) {
User user = new User();
user.userId = userCursor.getInt(0);
user.userName = userCursor.getString(1);
user.isMale = userCursor.getInt(2) == 1;
Log.d(TAG, "query user:" + user.toString());
}
userCursor.close();
}
(3)创建SQLiteOpenHelper (数据库帮助类)
管理数据库的方法 创建,升级,和降级
public class DBOpenHelper extends SQLiteOpenHelper { private static final String DB_name = "book_provider.db"; // 声明数据库 public static final String BookTabelName = "book"; //创建 一个数据 表 book public static final String User_TabelName = "user";//创建 一个数据 表 user private static final int DB_Version = 1; // 数据库的版本号 private String CREATE_BOOK_Table = "CREATE TABLE IF NOT EXISTS " + BookTabelName + "(_ID INTEGER PRIMARY KEY," + "name Text)"; private String CREATE_BOOK_User = "CREATE TABLE IF NOT EXISTS " + User_TabelName + "(_ID INTEGER PRIMARY KEY," + "name Text" + "sex INT)"; public DBOpenHelper(Context context) { super(context, DB_name, null, DB_Version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_BOOK_Table); //创建book表并且 创建参数 db.execSQL(CREATE_BOOK_User); //创建user表并且 创建参数 } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // 数据库升级的方法 }
为了 让外界能够区分要访问的数据 需要通过UriMatcher 来定义 Uri 和Uri_Code 使他们可以进行关联 。
provider 类里
public static final String AUTHORITY="com.com.example.liq.apfirst.Provider.BookProvider"; public static final Uri BOOK_CONTENT_URI=Uri.parse("content://"+AUTHORITY+"/book"); public static final Uri USER_CONTENT_URI=Uri.parse("content://"+AUTHORITY+"/user");
public static final int BOOK_CODE=0; public static final int USER_CODE=1; public static final UriMatcher sURI_MATCHER=new UriMatcher(UriMatcher.NO_MATCH);/常量UriMatcher.NO_MATCH = 不匹配任何路径的返回码 static { sURI_MATCHER.addURI(AUTHORITY,"book",BOOK_CODE); sURI_MATCHER.addURI(AUTHORITY,"user",USER_CODE); }
这个时候 数据Uri 和Uri_Code 已经进行关联成功了 下面这段代码意思 是 根据uri 取出 uri_code 在根据 uri_code 得到数据库的名称。
private String getTableName(Uri uri){ String TableName=null; switch (sURI_MATCHER.match(uri)){ case BOOK_CODE: //访问book 表 TableName=DBOpenHelper.BookTabelName; break; case USER_CODE: //访问 user 表 TableName=DBOpenHelper.User_TabelName; break; } return TableName; }