ContentProvider
属于 Android
的四大组件之一,基于 Android
中的Binder
机制实现。主要应用于进程间数据传输。
当然Android四大组件都能实现进程间的通信,并且底层都是基于Binder来实现的。
一.统一资源标识符(URI)
使用ContentProvider
需要先介绍下统一资源标识符(URI)。
URI
分为 系统预置 & 自定义,分别对应系统内置的数据(如通讯录、日程表等等)和自定义数据库
拿下面案例中的URI举例content://com.example.myprovider/user
:
content
: URI前缀。
com.example.myprovider
: 自定义ContentProvider唯一标识。
user
:自定义的数据库表名。
二. 自定义应用内ContentProvider
1. 创建一个DBHeler类继承自SQLiteOpenHelper。
用于创建SqlLite数据库操作。
public class DBHelper extends SQLiteOpenHelper {
// 数据库名
private static final String DATABASE_NAME = "com_example_content_provider.db";
// 表名
public static final String USER_TABLE_NAME = "user";
//数据库版本号
private static final int DATABASE_VERSION = 1;
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
// 创建 用户表
db.execSQL("CREATE TABLE IF NOT EXISTS " + USER_TABLE_NAME + "(_id INTEGER PRIMARY KEY AUTOINCREMENT," + " name TEXT)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
2.创建MyProvider继承自ContentProvider
主要是实现对DB数据增、删、改、查操作的封装。
public class MyProvider extends ContentProvider {
public static final String TAG = "MyProvider_ContentProvider";
private Context mContext;
DBHelper mDbHelper = null;
SQLiteDatabase db = null;
// 设置ContentProvider的唯一标识
public static final String AUTOHORITY = "com.example.myprovider";
public static final int User_Code = 1;
// UriMatcher类使用:在ContentProvider 中注册URI
private static final UriMatcher mMatcher;
static {
// 初始化
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 若URI资源路径 = content://com.example.myprovider/user ,则返回注册码User_Code
mMatcher.addURI(AUTOHORITY, "user", User_Code);
}
// 以下是ContentProvider的6个方法
/**
* 初始化ContentProvider
*/
@Override
public boolean onCreate() {
mContext = getContext();
// 在ContentProvider创建时对数据库进行初始化
// 注意:运行在主线程,不能做耗时操作
mDbHelper = new DBHelper(getContext());
db = mDbHelper.getWritableDatabase();
// 初始化两个表的数据(先清空两个表,再各加入一个记录)
db.execSQL("delete from user");
db.execSQL("insert into user values(1,'张三');");
db.execSQL("insert into user values(2,'李四');");
Log.d(TAG,"onCreate");
return true;
}
/**
* 添加数据
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
// 根据URI匹配 URI_CODE,从而匹配ContentProvider中相应的表名
String table = getTableName(uri);
// 向该表添加数据
db.insert(table, null, values);
// 当该URI的ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
mContext.getContentResolver().notifyChange(uri, null);
Log.d(TAG,"insert uri:"+uri);
return uri;
}
/**
* 查询数据
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// 根据URI匹配 URI_CODE,从而匹配ContentProvider中相应的表名
String table = getTableName(uri);
Log.d(TAG,"query uri:"+uri);
// 查询数据
return db.query(table, projection, selection, selectionArgs, null, null, sortOrder, null);
}
/**
* 更新数据
*/
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
Log.d(TAG,"update");
return 0;
}
/**
* 删除数据
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.d(TAG,"delete");
return 0;
}
@Override
public String getType(Uri uri) {
Log.d(TAG,"getType");
return null;
}
/**
* 根据URI匹配 URI_CODE,从而匹配ContentProvider中相应的表名
*/
private String getTableName(Uri uri) {
String tableName = null;
switch (mMatcher.match(uri)) {
case User_Code:
tableName = DBHelper.USER_TABLE_NAME;
break;
}
Log.d(TAG,"getTableName uri:"+uri);
return tableName;
}
}
ContentProvider组件需要在清单文件声明:
<provider android:name=".contentprovider.MyProvider"
android:authorities="com.example.myprovider"
/>
3.创建MyObserver继承自ContentObserver
看上面MyProvider类的public Uri insert(Uri uri, ContentValues values)
方法中代码
// 当该URI的ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
mContext.getContentResolver().notifyChange(uri, null);
也就是在调用MyObserver插入数据时候,ContentProvider会对外发送一个通知,也就是我们可以通过ContentObserver监听到对应的uri的数据变化信息。故创建MyObserver实现URI数据发生增加的时候,在MyObserver中打印出目前URI里的数据信息。
public class MyObserver extends ContentObserver{
public static final String TAG = "MyObserver_ContentProvider";
/**
* Creates a content observer.
*
* @param handler The handler to run {@link #onChange} on, or null if none.
*/
public MyObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
// 设置URI
Uri uri_user = Uri.parse("content://com.example.myprovider/user");
// 获取ContentResolver
ContentResolver resolver = MyApplication.getContext().getContentResolver();
// 通过ContentResolver 向ContentProvider中查询数据
Cursor cursor = resolver.query(uri_user, new String[]{"_id", "name"}, null, null, null);
while (cursor.moveToNext()) {
// 将表中数据全部输出
Log.d(TAG,"query user:" + cursor.getInt(0) + " " + cursor.getString(1));
}
// 关闭游标
cursor.close();
}
}
4.最后在调用ContentProvider打印出DB数据库的数据
新增一条数据,并打印当前URI对应的数据库表的所有数据。
// 设置URI
Uri uri_user = Uri.parse("content://com.example.myprovider/user");
// 插入表中数据
ContentValues values = new ContentValues();
values.put("_id", 3);
values.put("name", "王五");
// 获取ContentResolver
ContentResolver resolver = getContentResolver();
// 通过ContentResolver 根据URI 向ContentProvider中插入数据
resolver.insert(uri_user, values);
// 通过ContentResolver 向ContentProvider中查询数据
Cursor cursor = resolver.query(uri_user, new String[]{"_id", "name"}, null, null, null);
while (cursor.moveToNext()) {
// 将表中数据全部输出
Log.d(TAG,"query user:" + cursor.getInt(0) + " " + cursor.getString(1));
}
// 关闭游标
cursor.close();
当然,我们需要监听getContentResolver().notifyChange(uri, null),所以注册MyObserver。
private MyObserver myObserver;
//注册
myObserver = new MyObserver(new Handler());
getContentResolver().registerContentObserver(Uri.parse("content://com.example.myprovider/user"), false, myObserver);
//解绑
if (myObserver!=null) {
this.getContentResolver().unregisterContentObserver(myObserver);
}
以上就是对ContentProvider的基本使用的介绍。如果你还感兴趣监听系统ContentObserver的使用方式,可以再看看我的这篇文章,# Android开机自启动的两种方式,里面对系统手势导航结束标识位Settings.System.getUriFor("device_provisioned")
作了介绍:
传送门: # Android开机自启动的两种方式