Android 进阶之 ContentProvider 难点
2019-04-08 本文已影响11人
Kevin_小飞象
简介
ContentProvider 是 Android 四大组件之一,它通过 Binder 向其他组件乃至其他应用提供数据,当 ContentProvider 所在的进程启动的时候,ContentProvider 会同时启动并且发布到 AMS 中,需要注意的是,这个时候 ContentProvider 的onCreate 要先于 Application 的 onCreate 执行,这是四大组件一个少有的现象。
ContentProvider 的六个抽象方法:
- onCreate()
在创建 ContentProvider 时使用 - query()
用于查询指定 uri 的数据返回一个 Cursor - update()
用户更新指定uri的数据 - insert()
用于向指定 uri 的 ContentProvider 中添加数据 - delete()
用于删除指定uri的数据 - getType()
用于返回指定的Uri中的数据MIME类型
ContentProvider 对底层的数据存储方式没有任何要求,既可以使用 SQLite 数据库,也可以使用普通文件,甚至可以采用内存中的一个对象来进行数据存储。
简单实现
1. 新建一个项目,完成自定义 StudentContentProvider.java
public class StudentContentProvider extends ContentProvider {
//这里的AUTHORITY就是我们在AndroidManifest.xml中配置的authorities
private static final String AUTHORITY = "com.jrmf360.studentProvider";
//匹配成功后的匹配码
private static final int MATCH_CODE = 100;
private static UriMatcher uriMatcher;
private StudentDao studentDao;
//数据改变后指定通知的Uri
private static final Uri NOTIFY_URI = Uri.parse("content://" + AUTHORITY + "/student");
static {
//匹配不成功返回NO_MATCH(-1)
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//添加我们需要匹配的uri
uriMatcher.addURI(AUTHORITY,"student", MATCH_CODE);
}
@Override
public boolean onCreate() {
studentDao = StudentDao.getInstance(getContext());
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder) {
int match = uriMatcher.match(uri);
if (match == MATCH_CODE){
Cursor cursor = studentDao.queryStudent();
return cursor;
}
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
if (uriMatcher.match(uri) == MATCH_CODE){
studentDao.insertStudent(values);
notifyChange();
}
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
if (uriMatcher.match(uri) == MATCH_CODE){
int delCount = studentDao.deleteStudent();
notifyChange();
return delCount;
}
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
@Nullable String[] selectionArgs) {
return 0;
}
// 在数据库发生变化的时候我们调用notifyChange方法
private void notifyChange(){
getContext().getContentResolver().notifyChange(NOTIFY_URI,null);
}
}
通过其他的应用操作 ContentProvider 中的数据
新建一个项目,在 MainActivity.java 中操作之前自定义的 ContentProvider。
- 获得 ContentObserver
contentResolver = getContentResolver();
- 注册 ContentObserver 来监听对应 uri 的数据变化
private static final String AUTHORITY = "com.jrmf360.studentProvider";
private static final Uri STUDENT_URI = Uri.parse("content://" + AUTHORITY + "/student");
contentResolver.registerContentObserver(STUDENT_URI, true, new MyContentObserver(handler));
- MyContentObserver.java
public class MyContentObserver extends ContentObserver {
Handler mHandler;
public MyContentObserver(Handler handler) {
super(handler);
mHandler = handler;
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
Message message = Message.obtain();
message.obj = uri;
mHandler.sendMessage(message);
}
}
通过 ContentProvider 来操作数据
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.btn_query) {
Cursor cursor = contentResolver.query(STUDENT_URI, null, null, null, null, null);
if (cursor != null && cursor.getCount() > 0) {
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex("name"));
int age = cursor.getInt(cursor.getColumnIndex("age"));
String desc = cursor.getString(cursor.getColumnIndex("desc"));
Log.e(getClass().getSimpleName(), "Student:" + "name = " + name + ",age = " + age + ",desc = " + desc);
}
cursor.close();
}
} else if (id == R.id.btn_insert) {
ContentValues contentValues = new ContentValues();
contentValues.put("name", "新插入");
contentValues.put("age", "-100");
contentValues.put("desc", "我是新插入的呦。。。");
contentResolver.insert(STUDENT_URI, contentValues);
} else if (id == R.id.btn_del) {
contentResolver.delete(STUDENT_URI, null, null);
}
}
注意:
- 当通过增删改方法导致 ContentProvider 数据发生变化时需要通过 ContentResolver 的notifyChange 方法来通知外界数据发生改变。
- 可以调用 ContentResolver 的 registerContentObserver 方法来注册观察者,通过unregisterContentObserver方法来反注册观察者。
- query、update、insert、delete 存在多线程并发访问,需要做好线程同步。