ContentProvider应用组件实例记录

来源:互联网 发布:写jquery必须会js么 编辑:程序博客网 时间:2024/06/05 19:35

如果你公司开发了多款应用且应用间需要共享数据,如果你的应用中存在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
原创粉丝点击