ContentProvider總結(jié)

ContentProvider

ContentProvider作為四大組件之一,作用是IPC(跨進程通信),底層實現(xiàn)是Binder。

Android提供了ContentProvider,一個程序可以通過實現(xiàn)一個Content provider的抽象接口將自己的數(shù)據(jù)完全暴露出去,而且Content providers是以類似數(shù)據(jù)庫中表的方式將數(shù)據(jù)暴露战得。

ContentProvider中的URI
image.png

URI:是網(wǎng)絡資源的定義
Authority:授權(quán)信息,用以區(qū)別不同的ContentProvider将饺;
Path:表名贡避,用以區(qū)分ContentProvider中不同的數(shù)據(jù)表;
Id:Id號予弧,用以區(qū)別表中的不同數(shù)據(jù)刮吧;

自定義ContentProvider必須要實現(xiàn)6個方法:
public class MyContentProvider extends ContentProvider{

    @Override
    public boolean onCreate() {
        return false;
    }

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

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

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        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;
    }
}
ContentResolver

Content providers是以類似數(shù)據(jù)庫中表的方式將數(shù)據(jù)暴露出去,那么ContentResolver也將采用類似數(shù)據(jù)庫的操作來從Content providers中獲取數(shù)據(jù)掖蛤。

這里常使用到query操作:
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 

uri :查詢地址 (Uri)
projection :查詢的數(shù)據(jù)字段名稱 (String[] )
selection :查詢的條件 (String)
selectionArgs :查詢條件的參數(shù) (String[])
sortOrder :排序 (String)

ContentProvider杀捻、ContentResolver、ContentObserver 之間的關系

ContentProvider——內(nèi)容提供者蚓庭, 在android中的作用是對外共享數(shù)據(jù)致讥,也就是說你可以通過ContentProvider把應用中的數(shù)據(jù)共享給其他應用訪問,其他應用可以通過ContentProvider 對你應用中的數(shù)據(jù)進行添刪改查器赞。
ContentResolver——內(nèi)容解析者垢袱, 其作用是按照一定規(guī)則訪問內(nèi)容提供者的數(shù)據(jù)(其實就是調(diào)用內(nèi)容提供者自定義的接口來操作它的數(shù)據(jù))。
ContentObserver——內(nèi)容觀察者港柜,目的是觀察(捕捉)特定Uri引起的數(shù)據(jù)庫的變化请契,繼而做一些相應的處理,它類似于數(shù)據(jù)庫技術中的觸發(fā)器(Trigger)夏醉,當ContentObserver所觀察的Uri發(fā)生變化時爽锥,便會觸發(fā)它。

使用案例:獲取手機聯(lián)系人和短信

1.首先需要添加權(quán)限

    <uses-permission android:name="android.permission.READ_SMS"/>
    <uses-permission android:name="android.permission.READ_CONTACTS"/>

2.需要詢問權(quán)限并同意

requestPermissions( arrayOf(Manifest.permission.READ_CONTACTS, Manifest.permission.READ_SMS), REQUEST_CODE)

查詢聯(lián)系人和短信的管理類:

object ExtendsContentProvider {

    @SuppressLint("Range")
    fun readMessages(context: Context) {
        //短信表的路徑
        var uri = Uri.parse("content://sms")
        //需要查詢的字段
        var projection = arrayOf("address", "date", "body")
        var cursor = context.contentResolver.query(uri, projection, null, null, null)
        cursor?.let {
            while (it.moveToNext()) {
                Log.i(
                    "minfo", it.getString(it.getColumnIndex("address"))
                            + it.getString(it.getColumnIndex("date"))
                            + it.getString(it.getColumnIndex("body"))
                )
            }
            it.close()
        }
    }

    @SuppressLint("Range")
    fun readContacts(context: Context) {
        //ContactsContract.CommonDataKinds.Phone 聯(lián)系人表
        var cursor: Cursor? = context.contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
            null, null, null, null)
        cursor?.let {
            while (it.moveToNext()) {
                //讀取通訊錄的姓名
                var name = it.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
                //讀取通訊錄的號碼
                var number = cursor.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
                Log.i("minfo", "$name--$number")
            }
        }
    }

/**
     * 模糊查詢聯(lián)系人
     */
    @SuppressLint("Range")
    fun getContactsFuzzyQueryByKey(context: Context, key: String) {
        //ContactsContract.CommonDataKinds.Phone 聯(lián)系人表
        val projection = arrayOf(
            ContactsContract.PhoneLookup.DISPLAY_NAME,
            ContactsContract.CommonDataKinds.Phone.NUMBER
        )
        val selection = StringBuilder()
        selection.append(ContactsContract.Contacts.DISPLAY_NAME)
        selection.append(" LIKE '%$key%' ")
        var cursor: Cursor? = context.contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
            projection, selection.toString(), null, null)
        cursor?.let {
            while (it.moveToNext()) {
                //讀取通訊錄的姓名
                var name = it.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
                //讀取通訊錄的號碼
                var number = cursor.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
                Log.i("minfo", "$name--$number")
            }
            it.close()
        }
    }

    /**
     * 模糊查詢短信
     */
    @SuppressLint("Range")
    fun getSmsFuzzyQueryByKey(context: Context, key: String) {
        //短信表的路徑
        var uri = Uri.parse("content://sms")
        //需要查詢的字段
        var projection = arrayOf(Telephony.Sms.ADDRESS, Telephony.Sms.BODY)
        val selection = java.lang.StringBuilder()
        selection.append(Telephony.Sms.BODY)
        selection.append(" LIKE '%$key%' ")
        var cursor = context.contentResolver.query(uri, projection, selection.toString(), null, null)
        cursor?.let {
            while (it.moveToNext()) {
                var number = it.getString(it.getColumnIndex(Telephony.Sms.ADDRESS))
                var content = it.getString(it.getColumnIndex(Telephony.Sms.BODY))
                Log.i("minfo", "$content--$number")
            }
            it.close()
        }
    }

}

contentProvider的初始化流程

Application的創(chuàng)建和執(zhí)行onCreate畔柔,來看看這段代碼

android.app.ActivityThread
         ......
            Application app;
            ......
            try {
                // If the app is being launched for full backup or restore, bring it up in
                // a restricted environment with the base application class.
                2.創(chuàng)建Application(內(nèi)部也是反射)
                app = data.info.makeApplication(data.restrictedBackupMode, null);
                ......
                if (!data.restrictedBackupMode) {
                    if (!ArrayUtils.isEmpty(data.providers)) {
                        本文關注點:初始化ContentProvider
                        installContentProviders(app, data.providers);
                    }
                }
                try {
                       3.初始化Application
                       mInstrumentation.callApplicationOnCreate(app);
                    } catch (Exception e) {
                        if (!mInstrumentation.onException(app, e)) {
                            throw new RuntimeException(
                              "Unable to create application " + app.getClass().getName()
                              + ": " + e.toString(), e);
                       }
                  }
                }finally {
                ......
            }
        }
     ......
 }

可以看到氯夷,執(zhí)行初始化ContentProvider的時機在makeApplication和callApplicationOnCreate直接,那最起碼可以得出一個結(jié)論:ContentProvider初始化的時機在Application的onCreate之前靶擦。

想要弄明白ContentProvider初始化做了什么腮考,那就需要去installContentProviders方法看一看了。

android.app.ActivityThread


    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();
        遍歷ProviderInfo列表
        for (ProviderInfo cpi : providers) {
            if (DEBUG_PROVIDER) {
                StringBuilder buf = new StringBuilder(128);
                buf.append("Pub ");
                buf.append(cpi.authority);
                buf.append(": ");
                buf.append(cpi.name);
                Log.i(TAG, buf.toString());
            }
            各自啟動
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                啟動后放入數(shù)組中
                results.add(cph);
            }
        }

        try {
            將數(shù)組傳給AMS
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

installContentProviders完成了通過installProvider方法完成ContentProvider的啟動玄捕,并且將啟動了的ContentProvider放在了數(shù)組中踩蔚,傳遞給了AMS,AMS內(nèi)部會將他們存起來桩盲,這樣外部調(diào)用者就可以直接從AMS中獲取ContentProvider了。

參考:
https://juejin.cn/post/6996512754899091487

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末席吴,一起剝皮案震驚了整個濱河市赌结,隨后出現(xiàn)的幾起案子捞蛋,更是在濱河造成了極大的恐慌,老刑警劉巖柬姚,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拟杉,死亡現(xiàn)場離奇詭異,居然都是意外死亡量承,警方通過查閱死者的電腦和手機搬设,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來撕捍,“玉大人拿穴,你說我怎么就攤上這事∮欠纾” “怎么了默色?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長狮腿。 經(jīng)常有香客問我腿宰,道長,這世上最難降的妖魔是什么缘厢? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任吃度,我火速辦了婚禮,結(jié)果婚禮上贴硫,老公的妹妹穿的比我還像新娘椿每。我一直安慰自己,他們只是感情好夜畴,可當我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布拖刃。 她就那樣靜靜地躺著,像睡著了一般贪绘。 火紅的嫁衣襯著肌膚如雪兑牡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天税灌,我揣著相機與錄音均函,去河邊找鬼。 笑死菱涤,一個胖子當著我的面吹牛苞也,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播粘秆,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼如迟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起殷勘,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤此再,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后玲销,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體输拇,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年贤斜,在試婚紗的時候發(fā)現(xiàn)自己被綠了策吠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡瘩绒,死狀恐怖猴抹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情草讶,我是刑警寧澤洽糟,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站堕战,受9級特大地震影響坤溃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嘱丢,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一薪介、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧越驻,春花似錦汁政、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至并巍,卻和暖如春目木,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背懊渡。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工刽射, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人剃执。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓誓禁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親肾档。 傳聞我的和親對象是個殘疾皇子摹恰,可洞房花燭夜當晚...
    茶點故事閱讀 45,851評論 2 361

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