使用ContentProvider跨進(jìn)程共享數(shù)據(jù)

歡迎Follow我的GitHub, 關(guān)注我的簡(jiǎn)書. 其余參考Android目錄.

Logo

本文的合集已經(jīng)編著成書,高級(jí)Android開發(fā)強(qiáng)化實(shí)戰(zhàn),歡迎各位讀友的建議和指導(dǎo)。在京東即可購(gòu)買:https://item.jd.com/12385680.html

Android

ContentProvider主要應(yīng)用于進(jìn)程間數(shù)據(jù)共享. 對(duì)于應(yīng)用而言, 多進(jìn)程并不會(huì)經(jīng)常使用, 因而較少使用ContentProvider, 是最不常見的四大組件(Activity, Service, BroadcastReceiver, ContentProvider). 但是其優(yōu)異的性能與便捷, 對(duì)于多應(yīng)用共享數(shù)據(jù)而言, 非常重要, 比如共享同一份計(jì)步數(shù)據(jù)等. 開發(fā)者只有掌握多種技能, 才能在開發(fā)中游刃有余, 用最優(yōu)的方式完成項(xiàng)目, 提升應(yīng)用性能, 間接提高用戶體驗(yàn). 本文借用Demo, 講解ContentProvider共享數(shù)據(jù)的要點(diǎn).

本文源碼的GitHub下載地址


SQLite

ContentProvider需要媒介進(jìn)行數(shù)據(jù)存儲(chǔ), 最常用的就是SQLite數(shù)據(jù)庫(kù).

SQLite數(shù)據(jù)庫(kù)繼承SQLiteOpenHelper類, 提供數(shù)據(jù)庫(kù)名稱, 表名, 版本. 在onCreate方法中, 創(chuàng)建數(shù)據(jù)庫(kù)表, 添加字段.

本示例使用兩張表, 書籍和用戶.

public class DbOpenHelper extends SQLiteOpenHelper {
    private static final String DB_NAME = "book_provider.db";
    public static final String BOOK_TABLE_NAME = "book";
    public static final String USER_TABLE_NAME = "user";

    private static final int DB_VERSION = 1;

    private String CREATE_BOOK_TABLE = "CREATE TABLE IF NOT EXISTS "
            + BOOK_TABLE_NAME + "(_id INTEGER PRIMARY KEY, name TEXT)";

    private String CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS "
            + USER_TABLE_NAME + "(_id INTEGER PRIMARY KEY, name TEXT, sex INT)";

    public DbOpenHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    @Override public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK_TABLE);
        db.execSQL(CREATE_USER_TABLE);
    }

    @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

直接使用數(shù)據(jù)庫(kù)的情況較少, 也比較復(fù)雜, 推薦使用一些經(jīng)典ORM數(shù)據(jù)庫(kù), 如Sugar等, 簡(jiǎn)化管理. ORM, 即對(duì)象關(guān)系映射.


ContentProvider

ContentProvider提供數(shù)據(jù)訪問的接口, CRUD增刪改查. 在onCreate中, 初始化數(shù)據(jù)庫(kù), 并添加數(shù)據(jù).

@Override public boolean onCreate() {
    showLogs("onCreate 當(dāng)前線程: " + Thread.currentThread().getName());
    mContext = getContext();

    initProviderData(); // 初始化Provider數(shù)據(jù)

    return false;
}

private void initProviderData() {
    mDb = new DbOpenHelper(mContext).getWritableDatabase();
    mDb.execSQL("delete from " + DbOpenHelper.BOOK_TABLE_NAME);
    mDb.execSQL("delete from " + DbOpenHelper.USER_TABLE_NAME);
    mDb.execSQL("insert into book values(3,'Android');");
    mDb.execSQL("insert into book values(4, 'iOS');");
    mDb.execSQL("insert into book values(5, 'HTML5');");
    mDb.execSQL("insert into user values(1, 'Spike', 1);");
    mDb.execSQL("insert into user values(2, 'Wang', 0);");
}

CRUD的參數(shù)是Uri, 數(shù)據(jù)庫(kù)需要使用表名, 為了便于從Uri映射到表名, 使用關(guān)系轉(zhuǎn)換.

private String getTableName(Uri uri) {
    String tableName = null;
    switch (sUriMatcher.match(uri)) {
        case BOOK_URI_CODE:
            tableName = DbOpenHelper.BOOK_TABLE_NAME;
            break;
        case USER_URI_CODE:
            tableName = DbOpenHelper.USER_TABLE_NAME;
            break;
        default:
            break;
    }
    return tableName;
}

添加數(shù)據(jù)insert, 可以注冊(cè)內(nèi)容改變的監(jiān)聽, 插入數(shù)據(jù)時(shí), 廣播更新, 即notifyChange.

@Nullable @Override public Uri insert(Uri uri, ContentValues values) {
    showLogs("insert");
    String table = getTableName(uri);
    if (TextUtils.isEmpty(table)) {
        throw new IllegalArgumentException("Unsupported URI: " + uri);
    }
    mDb.insert(table, null, values);

    // 插入數(shù)據(jù)后通知改變
    mContext.getContentResolver().notifyChange(uri, null);
    return null;
}

刪除數(shù)據(jù)delete, 返回刪除數(shù)據(jù)的數(shù)量, 大于0即刪除成功.

@Override public int delete(Uri uri, String selection, String[] selectionArgs) {
    showLogs("delete");

    String table = getTableName(uri);
    if (TextUtils.isEmpty(table)) {
        throw new IllegalArgumentException("Unsupported URI: " + uri);
    }
    int count = mDb.delete(table, selection, selectionArgs);
    if (count > 0) {
        mContext.getContentResolver().notifyChange(uri, null);
    }

    return count; // 返回刪除的函數(shù)
}

修改數(shù)據(jù)update, 與刪除類似, 返回修改數(shù)據(jù)的數(shù)量.

@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    showLogs("update");

    String table = getTableName(uri);
    if (TextUtils.isEmpty(table)) {
        throw new IllegalArgumentException("Unsupported URI: " + uri);
    }
    int row = mDb.update(table, values, selection, selectionArgs);
    if (row > 0) {
        mContext.getContentResolver().notifyChange(uri, null);
    }

    return row; // 返回更新的行數(shù)
}

查詢數(shù)據(jù)query, 返回?cái)?shù)據(jù)庫(kù)的游標(biāo), 處理數(shù)據(jù).

@Nullable @Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    showLogs("query 當(dāng)前線程: " + Thread.currentThread().getName());
    String tableName = getTableName(uri);
    if (TextUtils.isEmpty(tableName)) {
        throw new IllegalArgumentException("Unsupported URI: " + uri);
    }
    return mDb.query(tableName, projection, selection, selectionArgs, null, null, sortOrder, null);
}

注意Uri和表名的轉(zhuǎn)換可能為空, 使用TextUtils.isEmpty判空.


共享數(shù)據(jù)

使用ContentProvider的獨(dú)立進(jìn)程, 模擬進(jìn)程間共享數(shù)據(jù).

<provider
    android:name=".BookProvider"
    android:authorities="org.wangchenlong.book.provider"
    android:permission="org.wangchenlong.BOOK_PROVIDER"
    android:process=":provider"/>

在AndroidManifest中, 把Provider注冊(cè)在:provider進(jìn)程中, 與主進(jìn)程分離.

添加數(shù)據(jù), 通過Uri找到ContentProvider, 使用ContentResolverinsert方法, 添加ContentValues數(shù)據(jù).

public void addBooks(View view) {
    Uri bookUri = BookProvider.BOOK_CONTENT_URI;
    ContentValues values = new ContentValues();
    values.put("_id", 6);
    values.put("name", "信仰上帝");
    getContentResolver().insert(bookUri, values);
}

查詢數(shù)據(jù)query, 與數(shù)據(jù)庫(kù)的使用方式類似, 解析出Cursor, 通過移動(dòng)Cursor, 找到所有匹配的結(jié)果.

public void showBooks(View view) {
    String content = "";
    Uri bookUri = BookProvider.BOOK_CONTENT_URI;
    Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "name"}, null, null, null);
    if (bookCursor != null) {
        while (bookCursor.moveToNext()) {
            Book book = new Book();
            book.bookId = bookCursor.getInt(0);
            book.bookName = bookCursor.getString(1);
            content += book.toString() + "\n";
            Log.e(TAG, "query book: " + book.toString());
            mTvShowBooks.setText(content);
        }
        bookCursor.close();
    }
}

效果

效果

ContentProvider封裝了跨進(jìn)程共享的邏輯, 我們只需要Uri即可訪問數(shù)據(jù), 使用共享數(shù)據(jù)非常便捷, 需要掌握簡(jiǎn)單的使用方式.

OK, that's all! Enjoy it!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 這篇主要介紹下ContentProvider如何實(shí)現(xiàn)共享數(shù)據(jù)、及ContentResolver如何訪問其他進(jìn)程等數(shù)...
    朋永閱讀 3,029評(píng)論 0 5
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,950評(píng)論 25 709
  • 很獨(dú)特的女性視角,讓我在大雪天窩在被窩里花了1.5點(diǎn)播,看完也不覺得后悔。 首先,女人對(duì)女人之間有種特別的情愫,這...
    深海綠妖閱讀 241評(píng)論 0 0
  • 頭頂翻涌的云層,濕悶的空氣,流水把時(shí)光不斷拉長(zhǎng),織成絲線,把我們纏繞,變成一個(gè)個(gè)晶瑩剔透的繭,等待破繭成蝶的一天…...
    爾非良人閱讀 260評(píng)論 6 0
  • 對(duì)孩子來(lái)說,大格局有哪些呢?我從雯雯身上感受到,令我感動(dòng)的品質(zhì),就是大格局。 參加活動(dòng),回家晚了,沒時(shí)間全家集體讀...
    上海雯雯媽M6閱讀 547評(píng)論 0 0

友情鏈接更多精彩內(nèi)容