ContentProvider应用组件实例记录

如果你公司开发了多款应用且应用间需要共享数据,如果你的应用中存在android:process=”:remote”这样的多进程的操作,是否还在忧愁如何传递数据这时候ContentProvider就可以派上用场了,贵为四大组件之一专门为不同应用不同进程共享数据使用。

首先我们需要了解URI的结构,因为ContentProvider每一个操作都跟URI有关系。

content://com.neacy.provider/books

一个典型的Uri结构的构造是以content://开头的然后接着是用于定位ContentProvider的唯一表示符,然后是路径标示,所以大体的结构体是:

content://authority-name/path-segment1/path-segment2/...

既然知道了URI的构造后如何解析呢?这里就需要引入UriMatcher类。

    /**
     * 定义Uri匹配
     */
    private static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    private static final int BOOK_COLLECT = 1;
    private static final int BOOK_SINGLE = 2;
    static {
        mUriMatcher.addURI(BookProviderMetaData.AUTHORITY, "books", BOOK_COLLECT);
        mUriMatcher.addURI(BookProviderMetaData.AUTHORITY, "books/#", BOOK_SINGLE);
    }

你告诉实例需要什么样的URI模式,并将对应的唯一标识符与每一个模式进行绑定,注册完这些模式之后,UriMatcher就是根据你传入的URI来进行模式匹配从而执行具体的业务代码。

switch (mUriMatcher.match(uri)) {
            case BOOK_COLLECT:
            //do something
                break;
            case BOOK_SINGLE:
            //do something
                break;
        }

我们实例中采用的SQLite数据库,所以我们需要先定义一个数据库需要用到的常量元数据类:

public class BookProviderMetaData {

    public static final String AUTHORITY = "com.neacy.provider";

    public static final String DATABASE_NAME = "book.db";
    public static final int DATABASE_VERSION = 1;
    public static final String DATABASE_TABLE = "books";

    /**
     * BaseColums内部自己提供了一个_id字段
     */
    public static final class BookTableMetaData implements BaseColumns {

        public static final String TABLE_NAME = "books";

        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/books");

        public static final String BOOK_NAME = "name";
        public static final String BOOK_ISBN = "isbn";
        public static final String BOOK_AUTHOR = "author";
    }
}

然后再构建数据库帮助类:

private static class BookDataHelper extends SQLiteOpenHelper {

        public BookDataHelper(Context context) {
            super(context, BookProviderMetaData.DATABASE_NAME, null, BookProviderMetaData.DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("create table books (" +
            BookProviderMetaData.BookTableMetaData._ID + " integer primary key, " +
            BookProviderMetaData.BookTableMetaData.BOOK_NAME + " text, " +
            BookProviderMetaData.BookTableMetaData.BOOK_ISBN + " text, " +
            BookProviderMetaData.BookTableMetaData.BOOK_AUTHOR + " text)");
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            db.execSQL("drop table if exists books");
            onCreate(db);
        }
    }

然后实现ContentProvider组件并复写其中的方法来实现功能:

@Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }

insert方法:
将数据记录插入到基础数据库中,并返回一个新创建的记录URI,数据记录采用ContentValues来存放。

public Uri insert(Uri uri, ContentValues values) {
        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        long rowId = db.insert(BookProviderMetaData.BookTableMetaData.TABLE_NAME, null, values);
        Uri result = ContentUris.withAppendedId(BookProviderMetaData.BookTableMetaData.CONTENT_URI, rowId);
        Log.w(TAG, "insert Uri Result = " + result);
        getContext().getContentResolver().notifyChange(result, null);
        return result;
    }

delete方法:
根据传入的where条件把对应的数据删除并返回删除的行数。

public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        int deleteId = 0;
        switch (mUriMatcher.match(uri)) {
            case BOOK_COLLECT:
                db.execSQL("DROP TABLE IF EXIST books");
                break;
            case BOOK_SINGLE:
                String id = uri.getPathSegments().get(1);
                deleteId = db.delete(BookProviderMetaData.BookTableMetaData.TABLE_NAME, BookProviderMetaData.BookTableMetaData._ID + "=" + id, selectionArgs);
                Log.w(TAG, "delete deleteId = " + deleteId);
                break;
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return deleteId;
    }

update方法
根据传入的ContentValues数据记录还有where条件来更新记录,并返回的是对应的行数。

public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        int updateId = 0;
        switch (mUriMatcher.match(uri)) {
            case BOOK_SINGLE:
                String id = uri.getPathSegments().get(1);
                updateId = db.update(BookProviderMetaData.BookTableMetaData.TABLE_NAME, values, BookProviderMetaData.BookTableMetaData._ID + "=" + id, selectionArgs);
                Log.w(TAG, "update updateId = " + updateId);
                break;
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return updateId;
    }

query方法
根据传入的URI或者和where语句一起返回Cursor数据:

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
        qb.setTables(BookProviderMetaData.BookTableMetaData.TABLE_NAME);
        qb.setProjectionMap(mBookProjectMap);
        switch (mUriMatcher.match(uri)) {
            case BOOK_COLLECT:
                break;
            case BOOK_SINGLE:
                qb.appendWhere(BookProviderMetaData.BookTableMetaData._ID + "=" + uri.getPathSegments().get(1));
                break;
        }
        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
        Cursor cursor = qb.query(db, projection, selection, selectionArgs, null, null, null);
        cursor.setNotificationUri(getContext().getContentResolver(), uri);
        Log.w(TAG, "query cursor count = " + cursor.getCount());
        return cursor;
    }

上面我们可以看出有这么一个方法:uri.getPathSegments()返回的就是URI后面的路径值。

还有那么一个方法:

getContext().getContentResolver().notifyChange(uri, null);

这个类的作用就是操作一条数据成功的时候实时发布通知出去,一种异步监听数据库中的数据发生了变动。

private static class BookContentObserver extends ContentObserver {

        /**
         * Creates a content observer.
         * @param handler The handler to run {@link #onChange} on, or null if none.
         */
        public BookContentObserver(Handler handler) {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            Log.w("Jayuchou", "--- onChange --- " + selfChange);

            /**
             * do something...
             */
        }
    }

我们可以发现他需要一个Handler作为将参数来构造的,因为内部采用了线程所以需要Handler将结果post到主线程中去。

getContentResolver().registerContentObserver(BookProviderMetaData.BookTableMetaData.CONTENT_URI, true, mObserver);

需要的三个参数分别是:
uri—-Uri类型,是需要监听的数据库的uri.
notifyForDescendents—boolean true的话就会监听所有与此uri相关的uri。false的话则是直接特殊的uri才会监听。一般都设置为true.
observer—–ContentObserver 就是需要的contentobserver.

protected void onDestroy() {
        super.onDestroy();
        getContentResolver().unregisterContentObserver(mObserver);// 移除监听 防止内存泄露
    }

当然需要在注册界面也要有移除注册防止内存泄露。

多进程多应用调用
首先在声明被调用的ContentProvider在注册的时候需要加入

android:exported="true"

类声明这个ContentProvider可以被外部调用使用,举一个查询的例子:

Cursor c = getContentResolver().query(Uri.parse("content://com.neacy.provider/books"), null, null, null, null);

操作其实相似主要ContentProvider路径要完成即可。

最后不要忘了声明注册ContentProvider

<provider android:authorities="com.neacy.provider"
                  android:name="com.provider.demo.BookProvider"
                  android:exported="true"/>

这样一个ContentProvider就完成了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值