ContentProvider、ContentResolver、ContentObserver三者之间的关系
ContentProvider 内容提供者,用于对外提供数据 。
ContentResolver 内容解析者,用于获取内容提供者提供的数据 。
ContentObserver 内容监听器,可以监听数据的改变状态 。
ContentProvider 是如何实现数据共享的
第一步:需要创建一个类继承ContentProvider,实现相关方法。
public class ContentProviderTest extends ContentProvider {
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
return null;
}
/**覆盖ContentProvider的getType方法对于用new Intent(String action, Uri uri)方法启动activity是很重要的,
如果它返回的MIME type和activity在<intent filter>中定义的data的MIME type不一致,将造成activity无法启动。
*/
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
@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;
}
}
第二步:在AndroidManifest.xml中注册ContentProvider,申明权限。
android:exported="true",让外部应用可以访问
android:authorities="com.king.qingpiao.contentProvider",申明URI中的authority
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.king.qiangpiao">
<!-- 声明一个权限 -->
<permission
android:name="com.king.qiangpiao.PRIVATE.all"
android:label="provider permission"
android:protectionLevel="normal" />
<permission
android:name="com.king.qiangpiao.PRIVATE.read"
android:label="provider read permission"
android:protectionLevel="normal" />
<permission
android:name="com.king.qiangpiao.PRIVATE.write"
android:label="provider write permission"
android:protectionLevel="normal" />
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<provider
android:authorities="com.king.qiangpiao.contentProvider"
android:name=".Book_ContentProvider"
android:readPermission="com.king.qiangpiao.PRIVATE.read"
android:writePermission="com.king.qiangpiao.PRIVATE.write"
android:permission="com.king.qiangpiao.PRIVATE.all"
android:exported="true">
</provider>
</application>
</manifest>
第三步:在其他应用中通过ContentResolver获取想要的数据。同时也需要给访问ContentProvider的权限。
需要使用ContentProvider的应用,必须在AndroidManifest.xml中添加ContentProvider中申明的权限。
<uses-permission android:name="com.king.qiangpiao.PRIVATE.all"/>
<uses-permission android:name="com.king.qiangpiao.PRIVATE.read"/>
<uses-permission android:name="com.king.qiangpiao.PRIVATE.write"/>
实例
在A应用中的Book_ContentProvider ,对外提供应用数据。在query中用SQLiteQueryBuilder进行映射查询。
其中getType方法对于用new Intent(String action, Uri uri)方法启动activity是很重要的,如果它返回的MIME type和activity在<intent filter>中定义的data的MIME type不一致,将造成activity无法启动。
具体的是注册内容就是第二步中配置。
public class Book_ContentProvider extends ContentProvider {
private static final String TAG = "Book_ContentProvider";
private static final String AUTHORITY = "com.king.qiangpiao.contentProvider";//在AndroidManifest.xml中注册authority
private static final Uri BOOK_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book");
private static final Uri USER_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user");
private static Map projectionMap;//查询映射,防止外部应用知道真正SQLite中的列表名
private Context mContext;
private SQLiteDatabase mDb;
private static final int BOOK_URI_CODE = 0;
private static final int USER_URI_CODE = 1;
private static final String BOOK_TABLE_NAME = "book";
private static final String USER_TABLE_NAME = "user";
//对URI的配对
private static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
mUriMatcher.addURI(AUTHORITY,BOOK_TABLE_NAME,BOOK_URI_CODE);
mUriMatcher.addURI(AUTHORITY,USER_TABLE_NAME,USER_URI_CODE);
}
@Override
public boolean onCreate() {
Log.i(TAG, "onCreate: Thread name = " + Thread.currentThread().getName());
mContext = getContext();
mDb = new DBHelper(mContext).getReadableDatabase();
projectionMap = new HashMap<String,String>();
projectionMap.put("book_id","_id");//外部 book_id 对应内部 _id
projectionMap.put("book_name","name");//外部 book_name 对应内部 name
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
//执行query , insert , delete , update是在不同的线程中的,有的操作需要注意这个问题
Log.i(TAG, "query: Thread name = " + Thread.currentThread().getName());
String table = getTableName(uri);
if(table == null){
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
//通过映射查询
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(BOOK_TABLE_NAME);
queryBuilder.setProjectionMap(projectionMap);
return queryBuilder.query(mDb,projection,selection,selectionArgs,null,null,sortOrder);
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
Log.i(TAG, "getType: Thread name = " + Thread.currentThread().getName());
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
Log.i(TAG, "insert: Thread name = " + Thread.currentThread().getName());
String table = getTableName(uri);
if(table == null){
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
Log.i(TAG, "insert: table = " + table + " , values = " + values.toString());
mDb.insert(table,null,values);
mContext.getContentResolver().notifyChange(uri,null);//通知ContentObserver数据变化
return uri;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
Log.i(TAG, "delete: Thread name = " + Thread.currentThread().getName());
String table = getTableName(uri);
if(table == null){
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
int count = mDb.delete(table,selection,selectionArgs);
if(count > 0){
mContext.getContentResolver().notifyChange(uri,null);//通知ContentObserver数据变化
}
return count;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
Log.i(TAG, "update: Thread name = " + Thread.currentThread().getName());
String table = getTableName(uri);
if(table == null){
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
int row = mDb.update(table,values,selection,selectionArgs);
if(row > 0){
mContext.getContentResolver().notifyChange(uri,null);//通知ContentObserver数据变化
}
return row;
}
//根据匹配返回数据库表名
private String getTableName(Uri uri){
int code = mUriMatcher.match(uri);
String tableName = null;
switch (code){
case BOOK_URI_CODE:
tableName = BOOK_TABLE_NAME;
break;
case USER_URI_CODE:
tableName = USER_TABLE_NAME;
break;
}
return tableName;
}
}
简单实现DBHelper,代码如下:
public class DBHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "database.db";
private static final String BOOK_TABLE_NAME = "book";
private static final String USER_TABLE_NAME = "user";
private static final int DB_VERSION = 1;
private static final String CREATE_BOOK_TABLE = "CREATE TABLE IF NOT EXISTS " + BOOK_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT)";
private static final String CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS " + USER_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT)";
public DBHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK_TABLE);
db.execSQL(CREATE_USER_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
应用B中,使用应用A的内容提供者,首先需要在应用B的AndroidManifest.xml中添加访问应用A的权限。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.chenpeng.contentresolverproject">
<uses-permission android:name="com.king.qiangpiao.PRIVATE.all"/>
<uses-permission android:name="com.king.qiangpiao.PRIVATE.read"/>
<uses-permission android:name="com.king.qiangpiao.PRIVATE.write"/>
.....
</manifest>
接下来就是在应用B中如何使用ContentResolver
public class MainActivity extends AppCompatActivity {
private Button btn1,btn2,btn3,btn4,btn5;
private ContentResolver mContentResolver;
private Uri mBookUri,mUseUri;
private static final String TAG = "MainActivity";
private DatabaseContentObserver mContentObserver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn1 = findViewById(R.id.btn1);
btn2 = findViewById(R.id.btn2);
btn3 = findViewById(R.id.btn3);
btn4 = findViewById(R.id.btn4);
btn5 = findViewById(R.id.btn5);
//内容监听器
mContentObserver = new DatabaseContentObserver(new Handler());
//内容解析者
mContentResolver = getContentResolver();
//Uri地址 对应应用A中定义的authorities值
mBookUri = Uri.parse("content://com.king.qiangpiao.contentProvider/book");
mUseUri = Uri.parse("content://com.king.qiangpiao.contentProvider/user");
//注册内容监听器
mContentResolver.registerContentObserver(mBookUri,true,mContentObserver);
//insert 插入数据到应用A中
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ContentValues bookValues = new ContentValues();
bookValues.put("_id",1);
bookValues.put("name","NetEasy");
mContentResolver.insert(mBookUri,bookValues);
ContentValues userValues = new ContentValues();
userValues.put("_id",1);
userValues.put("name","DingLei");
mContentResolver.insert(mUseUri,userValues);
}
});
//update 更新数据
btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ContentValues values = new ContentValues();
values.put("_id",1);
values.put("name","alibaba");
String[] selectionArgs = {String.valueOf(1)};
int count = mContentResolver.update(mBookUri,values,"_id = ?",selectionArgs);
Log.i(TAG, "update : " + count );
}
});
//delete 删除数据
btn3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int raw = mContentResolver.delete(mUseUri,"_id = ?" , new String[]{String.valueOf(1)});
Log.i(TAG, "delete : " + raw );
}
});
//query 查询
btn4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Cursor cursor = mContentResolver.query(mBookUri,new String[]{"book_id","book_name"},null,null,null);
while(cursor.moveToNext()){
int id = cursor.getInt(0);
String name = cursor.getString(1);
Log.i(TAG, "query: id = " + id + " , name = " + name);
}
cursor.close();
}
});
}
@Override
protected void onDestroy() {
//注销内容监听者
mContentResolver.unregisterContentObserver(mContentObserver);
super.onDestroy();
}
class DatabaseContentObserver extends ContentObserver{
public DatabaseContentObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
Log.i(TAG, "onChange: book 数据变化" );
super.onChange(selfChange);
}
}
}
网友评论