ContentProvider的使用

ContentProvider的意義

ContentProvider的類注釋如下:

Content providers are one of the primary building blocks of Android applications, providing content to applications. They encapsulate data and provide it to applications through the single ContentResolver interface. A content provider is only required if you need to share data between multiple applications. For example, the contacts data is used by multiple applications and must be stored in a content provider. If you don't need to share data amongst multiple applications you can use a database directly via android.database.sqlite.SQLiteDatabase

內(nèi)容提供者是Android應(yīng)用程序的主要構(gòu)建塊之一呻拌,為應(yīng)用程序提供內(nèi)容。它們封裝數(shù)據(jù)并通過單一的ContentResolver接口提供給應(yīng)用程序凝果。如果需要在多個應(yīng)用程序之間共享數(shù)據(jù)耘拇,則只需要內(nèi)容提供程序。例如污它,聯(lián)系人數(shù)據(jù)由多個應(yīng)用程序使用剖踊,必須存儲在內(nèi)容提供者中。如果您不需要在多個應(yīng)用程序之間共享數(shù)據(jù)衫贬,您可以直接通過數(shù)據(jù)庫使用數(shù)據(jù)庫德澈。

由此可見,ContentProvider適合進(jìn)程間的數(shù)據(jù)共享固惯,你的應(yīng)用可以通過ContentProvider將需要共享的部分?jǐn)?shù)據(jù)提供給其他應(yīng)用梆造。你也可以通過ContentProvider獲取手機(jī)的圖片、音視頻葬毫、通訊錄等數(shù)據(jù)

ContentProvider實(shí)現(xiàn)

需要重寫以下的幾個主要方法

  • onCreate 初始化創(chuàng)建ContentProvider
  • query 返回查詢數(shù)據(jù)
  • insert 插入數(shù)據(jù)
  • update 更新數(shù)據(jù)
  • delete 刪除數(shù)據(jù)
  • getType

getType方法很特別镇辉,基本用不上,但是它具體的作用又是什么呢贴捡,來看看它的注釋

Implement this to handle requests for the MIME type of the data at the
given URI. The returned MIME type should start with vnd.android.cursor.item for a single record, or vnd.android.cursor.dir/ for multiple items.
根據(jù)訪問的URI忽肛,返回對應(yīng)的MIME類型的字符串,這個字符串需要以vnd.android.cursor.item(返回單條數(shù)據(jù)記錄)或者vnd.android.cursor.dir/(返回多條數(shù)據(jù)記錄) 開頭

看到這些方法是不是很熟悉烂斋?跟數(shù)據(jù)庫操作一毛一樣啊屹逛,其實(shí)在這個時候我們可以將ContentProvider理解成一個共享數(shù)據(jù)庫础废,我們只要在ContentProvider初始化的時候,實(shí)例化一個可讀寫的數(shù)據(jù)庫罕模,在以上的方法中對這個數(shù)據(jù)庫進(jìn)行增刪改查的操作就可以了评腺。當(dāng)你的數(shù)據(jù)庫有多個表的時候,這些增刪改查又是如何區(qū)分不同的表呢淑掌?其實(shí)這些方法都有一個Uri的參數(shù)回調(diào)蒿讥,我們可以通過實(shí)例UriMatcher來區(qū)分用戶操作的到底是哪個表。

示例

創(chuàng)建一個DBOpenHelper


public static class TestDBOpenHelper extends SQLiteOpenHelper{
    public static final String DB_NAME = "test.db";
    public static final int VERSION=1;
    public static final String TABLE_NAME = "person";
    public static final String TABLE_NAME2 = "class";
    public static final String CREATE_TABLE = "create table if not exists "+TABLE_NAME+"(_id integer, name TEXT, desc TEXT)";
    public static final String CREATE_TABLE2= "create table if not exists "+TABLE_NAME2+"(id integer, className TEXT, desc TEXT)";

    public TestDBOpenHelper(Context context){

        super(context,DB_NAME,null,VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {

        db.execSQL(CREATE_TABLE);
        db.execSQL(CREATE_TABLE2);
    }

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

    }
}

ContentProvider


public class TestContentProvider extends ContentProvider {

        private static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        public static final String AUTHORITY = "com.gzkit.dailysample.TestContentProvider";  //授權(quán)
        public static final Uri PERSON_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/person");
        public static final Uri CLASS_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/class");

        private static final int TABLE_CODE_PERSON = 2;
        private static final int TABLE_CODE_CLASS = 3;

        static {
            //關(guān)聯(lián)不同的 URI 和 code抛腕,便于后續(xù) getType
            mUriMatcher.addURI(AUTHORITY, "person", TABLE_CODE_PERSON);
            mUriMatcher.addURI(AUTHORITY, "class", TABLE_CODE_CLASS);
        }

            //數(shù)據(jù)庫
        private SQLiteDatabase mDatabase;
        private Context mContext;
        private String mTable;

        private void initProvider(){

            mContext = getContext();
            mTable = TestDBOpenHelper.TABLE_NAME;
            mDatabase = new TestDBOpenHelper(mContext).getWritableDatabase();
                //插入一條初始數(shù)據(jù)
            mDatabase.execSQL("insert into "+mTable+" values(1,'test','good boy') ");


        }


        @Override
        public boolean onCreate() {

            initProvider();
            return false;
        }

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

            String tableName = getTableName(uri);


            return mDatabase.query(tableName,projection,selection,selectionArgs,null,sortOrder,null);
        }

        @Nullable
        @Override
        public String getType(@NonNull Uri uri) {
            int match = mUriMatcher.match(uri);
            String MIME = "";
            switch (match) {
                case TABLE_CODE_PERSON:

                    MIME = "vnd.android.cursor.dir/multi";
                    break;
                case TABLE_CODE_CLASS:
                    MIME = "vnd.android.cursor.item/single";

                    break;
            }

            return MIME;
        }

        @Nullable
        @Override
        public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
            String tableName = getTableName(uri);

            mDatabase.insert(tableName,null,values);
            //發(fā)送數(shù)據(jù)變動的通知
            mContext.getContentResolver().notifyChange(uri,null);
            return null;
        }

        @Override
        public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
            return 0;
        }

        @Override
        public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
            return 0;
        }

        /**
         * CRUD 的參數(shù)是 Uri芋绸,根據(jù) Uri 獲取對應(yīng)的表名
         *
         * @param uri
         * @return
         */
        private String getTableName(final Uri uri) {
            String tableName = "";
            int match = mUriMatcher.match(uri);
            switch (match){
                case TABLE_CODE_PERSON:
                    tableName = TestDBOpenHelper.TABLE_NAME;

                    break;

                case TABLE_CODE_CLASS:
                    tableName = TestDBOpenHelper.TABLE_NAME2;

                    break;
            }
            return tableName;
        }

在AndroidManifest中注冊ContentProvider

提供ContentProvider的APP按實(shí)際需要加入以下權(quán)限

<permission android:name="com.gzkit.dailysample.permission.READ_CONTENT"
    android:protectionLevel="normal"
    />
<permission android:name="com.gzkit.dailysample.permission.WRITE_CONTENT"
    android:protectionLevel="normal"
    />
<permission android:name="com.gzkit.dailysample.provider.permission"
    android:protectionLevel="normal"
    />

如果provider不存在這些權(quán)限的屬性,聲明這些權(quán)限則不是必要的

application節(jié)點(diǎn)下


<provider
   android:name="com.gzkit.dailysample.bean.TestContentProvider"
    android:authorities="com.gzkit.dailysample.TestContentProvider"
    android:exported="true"
    android:readPermission="com.gzkit.dailysample.permission.READ_CONTENT"
    android:writePermission="com.gzkit.dailysample.permission.WRITE_CONTENT"
    android:permission="com.gzkit.dailysample.provider.permission"
    android:process=":provider"
/>

  • name 自定義的類
  • authorities 定義授權(quán)的名稱兽埃,在自定義的ContentProvider中要用到
  • exported 外部是否能調(diào)用
  • process 進(jìn)程名
  • readPermission 讀取的權(quán)限名侥钳,必須和前面的permission節(jié)點(diǎn)名一致
  • writePermission 寫入的權(quán)限名,必須和前面的permission節(jié)點(diǎn)名一致
  • permission 讀寫的權(quán)限名柄错,必須和前面的permission節(jié)點(diǎn)名一致

值得注意的是舷夺,readPermission和writePermission的優(yōu)先級比permission高。

有以下幾種情況

  • 所有權(quán)限都不聲明售貌,其他應(yīng)用無須聲明權(quán)限就可以自由讀寫
  • 只聲明了permission给猾,則其他應(yīng)用需要聲明對應(yīng)的權(quán)限,即可讀寫
  • 只聲明readPermission颂跨,則其他應(yīng)用需要聲明此權(quán)限敢伸,才可以讀取數(shù)據(jù),但仍然可以寫入數(shù)據(jù)
  • 只聲明writePermission恒削,和上面相反
  • 聲明readPermission或writePermission池颈,同時聲明了permission,則其他應(yīng)用需要聲明對應(yīng)的權(quán)限钓丰,可以讀寫躯砰。

ContentProvider聲明了哪種權(quán)限,調(diào)用的那方就必須聲明對應(yīng)的權(quán)限

調(diào)用方需要聲明對應(yīng)的權(quán)限

<!--ContentProvider需要的權(quán)限-->
<uses-permission android:name="com.gzkit.dailysample.permission.READ_CONTENT"/>
<uses-permission android:name="com.gzkit.dailysample.permission.WRITE_CONTENT"/>
<uses-permission android:name="com.gzkit.dailysample.provider.permission"/>

使用ContentProvider

在activity或service中通過getContentResolver()獲取resolver接口進(jìn)行數(shù)據(jù)的增刪查改


public static final String AUTHORITY = "com.gzkit.dailysample.TestContentProvider";  //授權(quán)
public static final Uri PERSON_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/person");
public static final Uri CLASS_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/class");


public void insert(){
        ContentValues contentValues = new ContentValues();
        contentValues.put("_id",1);
        contentValues.put("name","test here");
        contentValues.put("desc","here is desc");
        getContentResolver().insert(PERSON_CONTENT_URI,contentValues);
}

public void query(){
        Cursor cursor =  getContentResolver().query(CLASS_CONTENT_URI,null,null,null,null);
        Toast.makeText(mContext, "查詢結(jié)果數(shù):"+cursor.getCount(), Toast.LENGTH_SHORT).show();
        cursor.close();
}

下一篇携丁,將會講到如何利用ContentProvider讀取手機(jī)的多媒體資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末琢歇,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子梦鉴,更是在濱河造成了極大的恐慌李茫,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肥橙,死亡現(xiàn)場離奇詭異魄宏,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)存筏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進(jìn)店門娜庇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來塔次,“玉大人,你說我怎么就攤上這事名秀。” “怎么了藕溅?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵匕得,是天一觀的道長。 經(jīng)常有香客問我巾表,道長汁掠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任集币,我火速辦了婚禮考阱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鞠苟。我一直安慰自己乞榨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布当娱。 她就那樣靜靜地躺著吃既,像睡著了一般。 火紅的嫁衣襯著肌膚如雪跨细。 梳的紋絲不亂的頭發(fā)上鹦倚,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天,我揣著相機(jī)與錄音冀惭,去河邊找鬼震叙。 笑死,一個胖子當(dāng)著我的面吹牛散休,可吹牛的內(nèi)容都是我干的媒楼。 我是一名探鬼主播,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼溃槐,長吁一口氣:“原來是場噩夢啊……” “哼匣砖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起昏滴,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤猴鲫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后谣殊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拂共,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年姻几,在試婚紗的時候發(fā)現(xiàn)自己被綠了宜狐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片势告。...
    茶點(diǎn)故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖抚恒,靈堂內(nèi)的尸體忽然破棺而出咱台,到底是詐尸還是另有隱情,我是刑警寧澤俭驮,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布回溺,位于F島的核電站,受9級特大地震影響混萝,放射性物質(zhì)發(fā)生泄漏遗遵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一逸嘀、第九天 我趴在偏房一處隱蔽的房頂上張望车要。 院中可真熱鬧,春花似錦崭倘、人聲如沸翼岁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽登澜。三九已至,卻和暖如春飘庄,著一層夾襖步出監(jiān)牢的瞬間脑蠕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工跪削, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谴仙,地道東北人。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓碾盐,卻偏偏與公主長得像晃跺,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子毫玖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,937評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Android程序往往都需要在androidmanifest.xml文件中來聲明許多的相關(guān)權(quán)限請求, 而權(quán)限請求也...
    Yinll閱讀 8,280評論 0 11
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,344評論 25 707
  • 參考Content Providers 對于ContentProvider, 可以把它看做為一個數(shù)據(jù)庫, 數(shù)據(jù)庫中...
    AssIstne閱讀 2,018評論 1 3
  • 獲取系統(tǒng)ContentProvider的例子(獲取聯(lián)系人及手機(jī)號) 自己創(chuàng)建的contentprovider掀虎,代碼...
    名字_都被占了閱讀 135評論 0 0
  • 一次過敏,一次強(qiáng)效的注射付枫,一天昏沉的睡眠烹玉。使我感覺,仿佛大病初愈對這人世間的一切又多了幾分的眷戀阐滩。 ...
    棕色小熊閱讀 215評論 1 0