ContentProvider的使用

1,369 阅读4分钟

ContentProvider属于 Android的四大组件之一,基于 Android中的Binder机制实现。主要应用于进程间数据传输。

当然Android四大组件都能实现进程间的通信,并且底层都是基于Binder来实现的。

一.统一资源标识符(URI)

使用ContentProvider需要先介绍下统一资源标识符(URI)。

aHR0cDovL3VwbG9hZC1pbWFnZXMuamlhbnNodS5pby91cGxvYWRfaW1hZ2VzLzk0NDM2NS05NjAxOWEyMDU0ZWIyN2NmLnBuZz9pbWFnZU1vZ3IyL2F1dG8tb3JpZW50L3N0cmlwJTdDaW1hZ2VWaWV3Mi8yL3cvMTI0MA.png

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开机自启动的两种方式