使用ContentProvider跨進程共享數據

歡迎Follow我的GitHub, 關注我的CSDN.

Content Provider

本文的合集已經編著成書,高級Android開發強化實戰,歡迎各位讀友的建議和指導。在京東即可購買:https://item.jd.com/12385680.html

Book

ContentProvider主要應用於進程間數據共享. 對於應用而言, 多進程並不會經常使用, 因而較少使用ContentProvider, 是最不常見的四大組件(Activity, Service, BroadcastReceiver, ContentProvider). 但是其優異的性能與便捷, 對於多應用共享數據而言, 非常重要, 比如共享同一份計步數據等. 開發者只有掌握多種技能, 才能在開發中遊刃有餘, 用最優的方式完成項目, 提升應用性能, 間接提高用戶體驗. 本文借用Demo, 講解ContentProvider共享數據的要點.

本文源碼的GitHub下載地址


SQLite

ContentProvider需要媒介進行數據存儲, 最常用的就是SQLite數據庫.

SQLite數據庫繼承SQLiteOpenHelper類, 提供數據庫名稱, 表名, 版本. 在onCreate方法中, 創建數據庫表, 添加字段.

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

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) {

    }
}

直接使用數據庫的情況較少, 也比較複雜, 推薦使用一些經典ORM數據庫, 如Sugar等, 簡化管理. ORM, 即對象關係映射.


ContentProvider

ContentProvider提供數據訪問的接口, CRUD增刪改查. 在onCreate中, 初始化數據庫, 並添加數據.

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

    initProviderData(); // 初始化Provider數據

    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的參數是Uri, 數據庫需要使用表名, 爲了便於從Uri映射到表名, 使用關係轉換.

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;
}

添加數據insert, 可以註冊內容改變的監聽, 插入數據時, 廣播更新, 即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);

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

刪除數據delete, 返回刪除數據的數量, 大於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; // 返回刪除的函數
}

修改數據update, 與刪除類似, 返回修改數據的數量.

@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; // 返回更新的行數
}

查詢數據query, 返回數據庫的遊標, 處理數據.

@Nullable @Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    showLogs("query 當前線程: " + 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和表名的轉換可能爲空, 使用TextUtils.isEmpty判空.


共享數據

使用ContentProvider的獨立進程, 模擬進程間共享數據.

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

在AndroidManifest中, 把Provider註冊在:provider進程中, 與主進程分離.

添加數據, 通過Uri找到ContentProvider, 使用ContentResolverinsert方法, 添加ContentValues數據.

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);
}

查詢數據query, 與數據庫的使用方式類似, 解析出Cursor, 通過移動Cursor, 找到所有匹配的結果.

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();
    }
}

效果

Demo

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

OK, that’s all! Enjoy it!

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