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就完成了。

發佈了79 篇原創文章 · 獲贊 105 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章