Android ContentProvider(三)

上一篇文章Android ContentProvider(二)學(xué)習(xí)了如何在自己的應(yīng)用程序中訪問其他程序的數(shù)據(jù),這篇文章主要介紹如何在自己的程序中創(chuàng)建ContentProvider共享數(shù)據(jù)。

創(chuàng)建MyProvider類

如果想要實現(xiàn)跨程序共享數(shù)據(jù)的功能,官方推薦的方式就是使用內(nèi)容提供器,可以通過新建一個類去繼承ContentProvider的方式來創(chuàng)建一個自己的內(nèi)容提供器。ContentProvider類中有六個抽象方法,我們在使用子類繼承它的時候,需要將這六個方法全部重寫。新建MyProvider繼承自ContentProvider,代碼如下所示:

public class MyProvider extends ContentProvider {
    
    @Override
    public boolean onCreate() {
        return false;
    }

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

    @Override
    public String getType(Uri uri) {
        return null;
    }

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

以上方法無非就是對數(shù)據(jù)的增刪改查,簡單介紹一下上面的6個方法。

  1. onCreate()
    初始化ContentProvider的時候調(diào)用。通常會在這里完成對數(shù)據(jù)庫的創(chuàng)建和升級等操作,返回true表示初始化成功,返回false則表示失敗。注意,只有當(dāng)存在ContentResolver嘗試訪問我們程序中的數(shù)據(jù)時,ContentProvider才會被初始化。
  2. query()
    從ContentProvider中查詢數(shù)據(jù)。使用uri參數(shù)來確定查詢哪張表,projection參數(shù)用于確定查詢哪些列,selection和selectionArgs參數(shù)用于約束查詢哪些行,sortOrder參數(shù)用于對結(jié)果進行排序,查詢的結(jié)果存放在Cursor對象中返回。
  3. getType()
    根據(jù)傳入的內(nèi)容URI來返回相應(yīng)的MIME類型。
  4. insert()
    向ContentProvider中添加一條數(shù)據(jù)。使用uri參數(shù)來確定要添加到的表,待添加的數(shù)據(jù)保存在values參數(shù)中。添加完成后,返回一個用于表示這條新記錄的URI。
  5. delete()
    從ContentProvider中刪除數(shù)據(jù)。使用uri參數(shù)來確定刪除哪一張表中的數(shù)據(jù),selection和selectionArgs參數(shù)用于約束刪除哪些行,被刪除的行數(shù)將作為返回值返回。
  6. update()
    更ContentProvider中已有的數(shù)據(jù)。使用uri參數(shù)來確定更新哪一張表中的數(shù)據(jù),新數(shù)據(jù)保存在values參數(shù)中,selection和selectionArgs參數(shù)用于約束更新哪些行,受影響的行數(shù)將作為返回值返回。

解析Uri參數(shù)

從上面的代碼中可以看到,除了onCreate(),每一個方法都會帶有Uri這個參數(shù), 這個參數(shù)也正是調(diào)用ContentResolver的增刪改查方法時傳遞過來的?,F(xiàn)在,我們需要對傳入的Uri參數(shù)進行解析,從中分析出調(diào)用方期望訪問的表和數(shù)據(jù)。

復(fù)習(xí)一下,一個標(biāo)準(zhǔn)的內(nèi)容URI寫法是這樣的:

content://com.example.app.provider/table1

這就表示調(diào)用方期望訪問的是com.example.app這個應(yīng)用的table1表中的數(shù)據(jù)。除此之外,我們還可以在這個內(nèi)容URI的后面加上一個id,如下所示:

content://com.example.app.provider/table1/1

這就表示調(diào)用方期望訪問的是com.example.app這個應(yīng)用的table1表中id為1的數(shù)據(jù)。

內(nèi)容URI的格式主要就只有以上兩種,以路徑結(jié)尾就表示期望訪問該表中所有的數(shù)據(jù),以id結(jié)尾就表示期望訪問該表中擁有相應(yīng)id的數(shù)據(jù)。我們可以使用通配符的方式來分別匹配這兩種格式的內(nèi)容URI,規(guī)則如下。

通配符 作用
* 匹配任意長度的任意字符
# 匹配任意長度的數(shù)字

所以,一個能夠匹配任意表的內(nèi)容URI格式就可以寫成:

content://com.example.app.provider/*

而一個能夠匹配 table1 表中任意一行數(shù)據(jù)的內(nèi)容URI格式就可以寫成:

content://com.example.app.provider/table1/#

接著再借助UriMatcher這個類就可以輕松地實現(xiàn)匹配內(nèi)容URI的功能。UriMatcher中提供了一個addURI()方法,這個方法接收三個參數(shù),可以分別把權(quán)限、路徑和一個自定義代碼傳進去。這樣,當(dāng)調(diào)用UriMatcher的match()方法時,就可以將一個Uri對象傳入,返回值是某個能夠匹配這個Uri對象所對應(yīng)的自定義代碼,利用這個代碼,我們就可以判斷出調(diào)用方期望訪問的是哪張表中的數(shù)據(jù)了。修改MyProvider中的代碼,如下所示:

public class MyProvider extends ContentProvider {

    public static final int TABLE1_DIR = 0;
    public static final int TABLE1_ITEM = 1;
    public static final int TABLE2_DIR = 2;
    public static final int TABLE2_ITEM = 3;

    private static UriMatcher uriMatcher;

    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI("com.example.app.provider", "table1", TABLE1_DIR);
        uriMatcher.addURI("com.example.app.provider ", "table1/#", TABLE1_ITEM);
        uriMatcher.addURI("com.example.app.provider ", "table2", TABLE2_DIR);
        uriMatcher.addURI("com.example.app.provider ", "table2/#", TABLE2_ITEM);
    }
    ……
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {

        switch (uriMatcher.match(uri)) {
            case TABLE1_DIR:
                //  查詢table1 表中的所有數(shù)據(jù)
                break;
            case TABLE1_ITEM:
                //  查詢table1 表中的單條數(shù)據(jù)
                break;
            case TABLE2_DIR:
                //  查詢table2 表中的所有數(shù)據(jù)
                break;
            case TABLE2_ITEM:
                //  查詢table2 表中的單條數(shù)據(jù)
                break;
            default:
                break;
        }
        ……
    }
    ……
}

可以看到,MyProvider中新增了四個整型常量,其中TABLE1_DIR表示訪問table1表中的所有數(shù)據(jù),TABLE1_ITEM表示訪問table1表中的單條數(shù)據(jù),TABLE2_DIR表示訪問table2表中的所有數(shù)據(jù),TABLE2_ITEM表示訪問table2表中的單條數(shù)據(jù)。接著在靜態(tài)代碼塊里創(chuàng)建了UriMatcher的實例,并調(diào)用addURI()方法,將期望匹配的內(nèi)容URI格式傳遞進去,注意這里傳入的路徑參數(shù)是可以使用通配符的。然后當(dāng)query()方法被調(diào)用的時候,就會通過UriMatcher的match()方法對傳入的Uri對象進行匹配,如果發(fā)現(xiàn) UriMatcher中某個內(nèi)容URI格式成功匹配了該Uri對象,則會返回相應(yīng)的自定義代碼,然后我們就可以判斷出調(diào)用方期望訪問的到底是什么數(shù)據(jù)了。

上述代碼只是以query()方法為例做了個示范,insert()、update()delete()這幾個方法的實現(xiàn)也是差不多的,它們都會攜帶Uri這個參數(shù),然后同樣利用UriMatcher的match()方法判斷出調(diào)用方期望訪問的是哪張表,再對該表中的數(shù)據(jù)進行相應(yīng)的操作就可以了。

getType()方法介紹

getType()方法,它是所有的ContentProvider都必須提供的一個方法,用于獲取Uri對象所對應(yīng)的MIME類型。一個內(nèi)容URI所對應(yīng)的MIME字符串主要由三部分組分,Android對這三個部分做了如下格式規(guī)定。
<b>1. 必須以vnd開頭。</b>
<b>2. 如果內(nèi)容URI以路徑結(jié)尾,則后接android.cursor.dir/,如果內(nèi)容URI以id結(jié)尾,則后接android.cursor.item/。</b>
<b>3. 最后接上 vnd.<authority>.<path>。</b>

所以,對于content://com.example.app.provider/table1這個內(nèi)容URI,它所對應(yīng)的MIME類型就可以寫成:

vnd.android.cursor.dir/vnd.com.example.app.provider.table1

對于content://com.example.app.provider/table1/1這個內(nèi)容URI,它所對應(yīng)的MIME類型就可以寫成:

vnd.android.cursor.item/vnd. com.example.app.provider.table1

現(xiàn)在我們可以繼續(xù)完善MyProvider中的內(nèi)容了,這次來實現(xiàn)getType()方法中的邏輯,代碼如下所示:

public class MyProvider extends ContentProvider {
    ……
    @Override
    public String getType(Uri uri) {
        switch (uriMatcher.match(uri)){
            case TABLE1_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.app.provider.table1";
            case TABLE1_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.app.provider.table1";
            case TABLE2_DIR:
                return "vnd.android.cursor.dir/vnd.com.example.app.provider.table2";
            case TABLE2_ITEM:
                return "vnd.android.cursor.item/vnd.com.example.app.provider.table2";
            default:
                break;
        }
        return null;
    }
    ……
}

到這里,一個完整的ContentProvider就算創(chuàng)建完成了。下一篇文章將通過實戰(zhàn)項目來體驗跨程序共享數(shù)據(jù)的功能。

如果文章對你有所幫助,那么請您點一下?
由于本人水平有限,如有錯誤,歡迎大家指正。如果你在操作過程中發(fā)現(xiàn)一些沒有講到的錯誤或者問題,歡迎在評論留言,一起探討,共同學(xué)習(xí)進步!

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

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

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