Content Provider 和 Content Resolver

Content Provider

提供了統一對外的數據訪問接口,能夠把本身應用的數據提供出去給的應用程序使用,能夠對你的數據進行增刪改查,例如 通信錄就是其中的一種。java

要想建立一個 Content Provider 須要進行下面幾部操做:android

  1. 繼承 ContentProvider 類,重寫 onCreate、getType、insert、update、query、delete 方法。
  2. 在 AndroidManifest.xml 文件中對 Provider 進行註冊。
    <provider android:name="classpath" android:authorities="com.exmaple.provider.applicationname" />
  3. 發佈 Content Provider 的 URI 地址,每一個Content Provider 都應該使用一個公有的靜態 CONTENT_URI 屬性來公開它的受權
    Public static final Uri CONTENT_URI = Uri.parse("content://com.exmaple.provider.applicationname/elements")

經過 Android 中的 Uri 訪問 Content Provider,Uri主要包含下面幾部分:
content://com.example.earthquake/contentprovider/quakes/1數據庫

  • scheme:Content Provider 的 scheme 是 content://
  • content:是 Content Provider 的惟一標示,外部程序能夠根據這個標示找到對應的 Content Provider
  • path:標示咱們要操做的數據

咱們通常提供所有查詢和指定條數兩種支持查詢模式:其中一個很是有用的類 UriMatcher,它是一個很是有用的類,能夠分析 URI 並肯定它的形勢。數據結構

// 建立兩個常量來區分不一樣的 URI 請求
private static final int ALLROWS = 1;
private static final int SINGLE_ROW = 2;

private static final UriMatcher uriMatcher;

// 填充 UriMatcher 對象,其中 element 結尾的 URI 對應請求全部的數據
// 以 elements/rowid 結尾的 URI 表明請求單行數據
static {
    uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    uriMatcher.addURI("com.exmaple.message.provider", "elements", ALLROWS);
    uriMatcher.addURI("com.exmaple.message.provider", "elements/#", SINGLE_ROW);
}

在同一個 Content Provider 中,可使用一樣的技術來公開其餘的 URI,這些 URI 表明了不一樣的數據子集或數據庫中不一樣的表。app

區分了全表和單行查詢後,就能夠很容易的使用 SQLiteQueryBuilder 類對一個查詢應用額外的選擇條件,以下面的代碼所示:ide

SQLiteQueryBuilder sqb = new SQLiteQueryBuilder();

// 若是是行查詢,用傳入的行限制結果集
switch (uriMatcher.match(uri)) {
case SINGLE_ROW:
    String rowID = uri.getPathSegments().get(1);
    sqb.appendWhere(KEY_ID + "=" + rowID);
default:
    break;
}

經常使用幫助方法:ui

UriMatcher:用於匹配對應的 Uri 路徑
ContentUris:用於操做 Uri 路徑後面的 id 部分spa

  • withAppendId 經過該方法把 主 Uri 和 對應的 id 傳入進去,組裝返回對應的 uri 對象

當應用程序啓動時,每一個 Content Provider 的 onCreate 處理程序會再應用程序的主線程中被調用。
和以前的數據庫操做同樣,最好使用 SQLiteOpenHelper 來延遲打開(必要的地方建立)底層的數據庫,直到 Content Provider 的查詢或事務方法須要時再打開或建立它。線程

@Override
public boolean onCreate() {
    // 構建一個底層數據庫
    // 延遲打開數據庫,直到須要執行
    // 一個查詢或者事務時再打開
    sqLiteHelper = new MessageSQLiteHelper(getContext(),
        MessageSQLiteHelper.DATABASE_NAME, null,
        MessageSQLiteHelper.DATABASE_VERSION);

    return true;
}


實現 Content Provider 查詢

要想使用 Content Provider 就必須實現 query 和 getType 方法。Content Provider 使用這些方法來訪問底層數據,無需知道底層的數據結構和實現。
Content Provider 的最多見場景就是訪問一個 SQLite 數據庫,但在這些方法中,你能夠訪問任何的數據庫(包括文件或應用程序實例變量)。
UriMatcher 對象應用於完善事務處理和查詢請求,而 SQLiteQueryBuilder 是執行基於行查詢的便利輔助類。code

public class StudentContentProvider extends ContentProvider {

    private SimpleSQLiteHelper sqLiteHelper;

    // 建立兩個常量來區分不一樣的 URI 請求
    public static final int ALLROWS = 1;
    public static final int SINGLE_ROW = 2;

    public static final Uri CONTENT_URI = Uri.parse("content://com.example.contentprovider/students");

    private static final UriMatcher uriMatcher;

    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI("com.example.contentprovider", "students", ALLROWS);
        uriMatcher.addURI("com.example.contentprovider", "students/#",
                SINGLE_ROW);
    }

    @Override
    public boolean onCreate() {
        sqLiteHelper = new SimpleSQLiteHelper(getContext(),
                SimpleSQLiteHelper.DATABASE_NAME, null,
                SimpleSQLiteHelper.DATABASE_VERSION);
        return true;
    }

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

        SQLiteDatabase db;
        try {
            db = sqLiteHelper.getWritableDatabase();
        } catch (SQLiteException e) {
            db = sqLiteHelper.getReadableDatabase();
        }

        // 設定分組和聚合條件
        String groupBy = null;
        String having = null;

        SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();

        switch (uriMatcher.match(uri)) {
        case SINGLE_ROW:
            String rowID = uri.getPathSegments().get(1);
            queryBuilder.appendWhere(SimpleSQLiteHelper.KEY_ID + " = " + rowID);
            break;

        default:
            break;
        }

        // 指定要執行查詢的表,根據須要,這能夠是一個特定的表或者一個鏈接
        queryBuilder.setTables(SimpleSQLiteHelper.DATABASE_TABLE);

        // 執行查詢操做
        Cursor cursor = queryBuilder.query(db, projection, selection,
                selectionArgs, groupBy, having, sortOrder);

        return cursor;
    }

    @Override
    public String getType(Uri uri) {
        // 爲一個 Content Provider URI 返回一個字符串,它標識了 MIME 類型
        switch (uriMatcher.match(uri)) {
        case ALLROWS:
            return "vnd.android.cursor.dir/vnd.example.contentprovider.elemental";

        case SINGLE_ROW:
            return "vnd.android.cursor.item/vnd.example.contentprovider.elemental";

        default:
            throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {

        // 打開一個可寫的數據庫鏈接
        SQLiteDatabase db = sqLiteHelper.getWritableDatabase();

        String nullColumnHack = null;

        // 向表中插入值
        long id = db.insert(SimpleSQLiteHelper.DATABASE_TABLE, nullColumnHack,
                values);

        if (id > -1) {

            // 構造並返回插入行的 URI
            Uri insertedId = ContentUris.withAppendedId(CONTENT_URI, id);

            // 通知全部的觀察者,數據集已經改變
            getContext().getContentResolver().notifyChange(insertedId, null);

            return insertedId;
        }

        return null;
    }

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

        // 打開一個可寫的數據庫鏈接
        SQLiteDatabase db = sqLiteHelper.getWritableDatabase();

        // 若是是行 URI,限定刪除行爲指定的行
        switch (uriMatcher.match(uri)) {
        case SINGLE_ROW:
            String rowID = uri.getPathSegments().get(1);
            selection = SimpleSQLiteHelper.KEY_ID + " = " + rowID
                    + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ")" : "");
            break;

        default:
            break;
        }

        // 要想返回刪除的項的數量,必須指定一條 where 子句,要刪除全部的行並返回一個值,則傳入 1
        if (selection == null) {
            selection = "1";
        }

        // 執行刪除操做
        int deleteCount = db.delete(SimpleSQLiteHelper.DATABASE_TABLE,
                selection, selectionArgs);

        // 通知全部的觀察者,數據集已經改變
        getContext().getContentResolver().notifyChange(uri, null);


        return deleteCount;
    }

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

        // 打開一個可寫的數據庫鏈接
        SQLiteDatabase db = sqLiteHelper.getWritableDatabase();

        // 若是是行 URI,限定刪除行爲指定的行
        switch (uriMatcher.match(uri)) {
        case SINGLE_ROW:
            String rowID = uri.getPathSegments().get(1);
            selection = SimpleSQLiteHelper.KEY_ID
                    + " = "
                    + rowID
                    + (!TextUtils.isEmpty(selection) ? " AND (" + selection
                            + ")" : "");
            break;

        default:
            break;
        }

        // 執行更新
        int updateCount = db.update(SimpleSQLiteHelper.DATABASE_TABLE, values,
                selection, selectionArgs);

        // 通知全部的觀察者,數據集已經改變
        getContext().getContentResolver().notifyChange(uri, null);

        return updateCount;
    }

}


Content Resolver

能夠經過 Content Resolver 來訪問 Content Provider 提供的數據。能夠經過 getContext().getContentResolver() 方法來獲取一個 Content Resolver 對象。


監聽 Content Provider 中的數據變化

若是 共享的數據發生變化,能夠在 Content Provider 發生數據變化時調用 getCotentResolver().notifyChange(uri, null) 來通知註冊在此 URI 上的訪問者

相關文章
相關標籤/搜索