Android 进阶之 ContentProvider 难点

简介

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 存在多线程并发访问,需要做好线程同步。
发布了225 篇原创文章 · 获赞 64 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/duoduo_11011/article/details/103950692