Android 這 13 道 ContentProvider 面試題削罩,你都會了嗎?

勞力越帶愿阐,責(zé)任越大

前言


  • 作為 Android 的四大組件之一趾疚,ContentProvider 可以說是無處不在了。
  • 但是對于我而言丛肮,開發(fā)過程中看似 ContentProvider 用得很嫻熟魄缚,卻一直沒能形成一個完整的體系冶匹。
  • 也許大家也有著和我類似的煩惱,于是我特地花了幾天的時間诽里,總結(jié)了我所知道的知識點飞蛹,以及面試中可能遇到的問題卧檐。將本文分享給大家,希望能幫助大家重新梳理下我們的這個老朋友 ContentProvider 拒贱。

希望大家閱讀愉快佛嬉!

文章目錄

  • ContentProvider 應(yīng)用程序間非常通用的共享數(shù)據(jù)的一種方式暖呕,也是 Android 官方推薦的方式。
  • Android 中許多系統(tǒng)應(yīng)用都使用該方式實現(xiàn)數(shù)據(jù)共享瓤逼,比如通訊錄霸旗、短信等戚揭。
ContentProvider

方便大家學(xué)習(xí)民晒,我在 GitHub 上建立個 倉庫


1.1 Android 為什么要設(shè)計 ContentProvider 這個組件拂蝎?

為什么要設(shè)計 ContentProvider
  • 很多做 Android 開發(fā)的人都不怎么使用它惶室,覺得直接讀取數(shù)據(jù)庫會更簡單方便皇钞。
  • 那么 Android 搞一個內(nèi)容提供者在數(shù)據(jù)和應(yīng)用之間鹅士,只是為了裝高大上,故弄玄虛也拜?我認為其設(shè)計用意在于:
  1. 封裝慢哈。對數(shù)據(jù)進行封裝永票,提供統(tǒng)一的接口侣集,使用者完全不必關(guān)心這些數(shù)據(jù)是在 DBXML 编振、Preferences 或者網(wǎng)絡(luò)請求來的踪央。當(dāng)項目需求要改變數(shù)據(jù)來源時瓢阴,使用我們的地方完全不需要修改荣恐。
  2. 提供一種跨進程數(shù)據(jù)共享的方式。
  3. 應(yīng)用程序間的數(shù)據(jù)共享還有另外的一個重要話題少漆,就是數(shù)據(jù)更新通知機制了检疫。因為數(shù)據(jù)是在多個應(yīng)用程序中共享的祷嘶,當(dāng)其中一個應(yīng)用程序改變了這些共享數(shù)據(jù)的時候论巍,它有責(zé)任通知其它應(yīng)用程序嘉汰,讓它們知道共享數(shù)據(jù)被修改了,這樣它們就可以作相應(yīng)的處理双泪。

1.2 如何訪問自定義 ContentProvider

如何訪問自定義 ContentProvider
  • ContentResolver 接口的 notifyChange 函數(shù)來通知那些注冊了監(jiān)控特定 URI的ContentObserver 對象焙矛,使得它們可以相應(yīng)地執(zhí)行一些處理村斟。
  • ContentObserver 可以通過 registerContentObserver 進行注冊。
  • 通過 ContentProviderUri 訪問開放的數(shù)據(jù)孩灯。
  1. ContenResolver 對象通過 Context 提供的方法 getContenResolver() 來獲得峰档。
  2. ContenResolver 提供了以下方法來操作:insert delete update query 這些方法分別會調(diào)用 ContenProvider 中與之對應(yīng)的方法并得到返回的結(jié)果匣距。

1.3 通過 ContentResolver 獲取 ContentProvider 內(nèi)容的基本步驟

ContentResolver 獲取 ContentProvider 內(nèi)容的基本步驟
  1. 得到 ContentResolver 類對象:ContentResolver cr = getContentResolver ( )毅待。
  2. 定義要查詢的字段 String 數(shù)組尸红。
  3. 使用 cr.query() ; 返回一個 Cursor 對象外里。
  4. 使用 while 循環(huán)得到 Cursor 里面的內(nèi)容。

1.4 ContentProvider 是如何實現(xiàn)數(shù)據(jù)共享的:

ContentProvider 是如何實現(xiàn)數(shù)據(jù)共享的
  • Android 中如果想將自己應(yīng)用的數(shù)據(jù) ( 一般多為數(shù)據(jù)庫中的數(shù)據(jù) ) 提供給第三發(fā)應(yīng)用, 那么我們只能通過 ContentProvider 來實現(xiàn)了鳖链。 ContentProvider 是應(yīng)用程序之間共享數(shù)據(jù)的接口芙委。
  • 使用的時候首先自定義 一個類繼承 ContentProvider , 然后覆寫 query 灌侣、insert 吴藻、updatedelete 等 方法皮壁。
  • 因為其是四大組件之一因此必須在 AndroidManifest 文件中進行注冊符喝。
  • 把自己的數(shù)據(jù)通過 uri 的形式共享出去 android 系統(tǒng)下 不同程序 數(shù)據(jù)默認是不能共享訪問 需要去實現(xiàn)一個類去繼承 ContentProvider协饲。
public class PersonContentProvider extends ContentProvider{

   public boolean onCreate(){ }
   query(Url, String[], String, String[], String);
   insert(Uri,ContentValues);
   update(Uri,ContentValues,String[]);
   delete(Uri,String,String[]);
   
} 

1.5 為什么要用 ContentProvider ?它和 sql 的實現(xiàn)上有什么差別?

為什么要用 ContentProvider ?它和 sql 的實現(xiàn)上有什么差別
  • ContentProvider 屏蔽了數(shù)據(jù)存儲的細節(jié) , 內(nèi)部實現(xiàn)對用戶完全透明 , 用戶只需要關(guān)心操作數(shù)據(jù)的 uri 就可以了, ContentProvider 可以實現(xiàn)不同 app之間 共享茉稠。
  • Sql 也有增刪改查的方法, 但是 sql 只能查詢本應(yīng)用下的數(shù)據(jù)庫而线。
  • ContentProvider 還可以去增刪改查本地文件. xml 文件的讀取等膀篮。

1.6 Uri 介紹

Uri 介紹
  • 為系統(tǒng)的每一個資源給其一個名字誓竿,比方說通話記錄谈截。
  1. 每一個 ContentProvider 都擁有一個公共的 URI 簸喂,這個 URI 用于表示這個 ContentProvider 所提供的數(shù)據(jù)喻鳄。
  2. Android 所提供的 ContentProvider 都存放在 android.provider 包中。
  • 將其分為 A再菊,B袄简,C泛啸,D 4個部分:
  • A:標(biāo)準(zhǔn)前綴,用來說明一個 Content Provider 控制這些數(shù)據(jù)种柑,無法改變的匹耕;"content://"稳其;
  • BURI 的標(biāo)識,用于唯一標(biāo)識這個 ContentProvider 煤傍,外部調(diào)用者可以根據(jù)這個標(biāo)識來找到它蚯姆。它定義了是哪個 ContentProvider 提供這些數(shù)據(jù)龄恋。對于第三方應(yīng)用程序凶伙,為了保證 URI 標(biāo)識的唯一性镊靴,它必須是一個完整的、小寫的類名煮落。這個標(biāo)識在元素的 authorities 屬性中說明:一般是定義該 ContentProvider 的包類的名稱蝉仇;
  • C:路徑( path )轿衔,通俗的講就是你要操作的數(shù)據(jù)庫中表的名字睦疫,或者你也可以自己定義蛤育,記得在使用的時候保持一致就可以了;"content://com.bing.provider.myprovider/tablename"腋么。
  • D:如果URI中包含表示需要獲取的記錄的 ID亥揖;則就返回該id對應(yīng)的數(shù)據(jù)费变,如果沒有 ID胡控,就表示返回全部; "content://com.bing.provider.myprovider/tablename/#" # 表示數(shù)據(jù) id

1.7 如何訪問 asserts 資源目錄下的數(shù)據(jù)庫?

訪問 asserts 資源目錄下的數(shù)據(jù)庫
  • 把數(shù)據(jù)庫 db 復(fù)制到 /data/data/packagename/databases/ 目錄下, 然后直接就能訪問了橙困。

1.8 多個進程同時調(diào)用一個 ContentProvider 的 query 獲取數(shù)據(jù)凡傅,ContentPrvoider 是如何反應(yīng)的呢夏跷?

調(diào)用一個 ContentProvider 的 query 獲取數(shù)據(jù)槽华,ContentPrvoider 是如何反應(yīng)的
  • 一個 ContentProvider 可以接受來自另外一個進程的數(shù)據(jù)請求猫态。
  • 盡管 ContentResolverContentProvider 類隱藏了實現(xiàn)細節(jié)披摄,但是 ContentProvider 所提供的 query()疚膊,insert()寓盗,delete()夺巩,update() 都是在 ContentProvider 進程的線程池中被調(diào)用執(zhí)行的柳譬,而不是進程的主線程中美澳。
  • 這個線程池是有 Binder 創(chuàng)建和維護的摸航,其實使用的就是每個應(yīng)用進程中的 Binder 線程池酱虎。

1.9 Android 設(shè)計 ContentProvider 的目的是什么呢读串?

設(shè)計 ContentProvider 的目的
  • 隱藏數(shù)據(jù)的實現(xiàn)方式恢暖,對外提供統(tǒng)一的數(shù)據(jù)訪問接口杰捂;
  • 更好的數(shù)據(jù)訪問權(quán)限管理。ContentProvider 可以對開發(fā)的數(shù)據(jù)進行權(quán)限設(shè)置挨队,不同的 URI 可以對應(yīng)不同的權(quán)限盛垦,只有符合權(quán)限要求的組件才能訪問到 ContentProvider 的具體操作熄浓。
  • ContentProvider 封裝了跨進程共享的邏輯赌蔑,我們只需要 Uri 即可訪問數(shù)據(jù)娃惯。由系統(tǒng)來管理 ContentProvider 的創(chuàng)建趾浅、生命周期及訪問的線程分配馒稍,簡化我們在應(yīng)用間共享數(shù)據(jù)( 進程間通信 )的方式纽谒。我們只管通過 ContentResolver 訪問 ContentProvider 所提示的數(shù)據(jù)接口鼓黔,而不需要擔(dān)心它所在進程是啟動還是未啟動澳化。

1.10 運行在主線程的 ContentProvider 為什么不會影響主線程的UI操作?

ContentProvider 為什么不會影響主線程的UI操作
  • ContentProvideronCreate() 是運行在 UI 線程的缎谷,而 query() 灶似,insert() 喻奥,delete() 撞蚕,update() 是運行在線程池中的工作線程的
  • 所以調(diào)用這向個方法并不會阻塞 ContentProvider 所在進程的主線程甥厦,但可能會阻塞調(diào)用者所在的進程的 UI 線程刀疙!
  • 所以扫倡,調(diào)用 ContentProvider 的操作仍然要放在子線程中去做撵溃。
  • 雖然直接的 CRUD 的操作是在工作線程的缘挑,但系統(tǒng)會讓你的調(diào)用線程等待這個異步的操作完成语淘,你才可以繼續(xù)線程之前的工作际歼。

1.11 外提供數(shù)據(jù)共享鹅心,那么如何限制對方的使用呢旭愧?

如何限制對方的使用
  • android:exported 屬性非常重要榕茧。這個屬性用于指示該服務(wù)是否能夠被其他應(yīng)用程序組件調(diào)用或跟它交互用押。

  • 如果設(shè)置為 true靶剑,則能夠被調(diào)用或交互桩引,否則不能坑匠。

  • 設(shè)置為 false 時厘灼,只有同一個應(yīng)用程序的組件或帶有相同用戶 ID 的應(yīng)用程序才能啟動或綁定該服務(wù)设凹。

  • 對于需要開放的組件應(yīng)設(shè)置合理的權(quán)限舰讹,如果只需要對同一個簽名的其它應(yīng)用開放 ContentProvider ,則可以設(shè)置 signature 級別的權(quán)限闪朱。

  • 大家可以參考一下系統(tǒng)自帶應(yīng)用的代碼月匣,自定義了 signature 級別的 permission

<permission android:name="com.android.gallery3d.filtershow.permission.READ"
            android:protectionLevel="signature" />

<permission android:name="com.android.gallery3d.filtershow.permission.WRITE"
            android:protectionLevel="signature" />

<provider
    android:name="com.android.gallery3d.filtershow.provider.SharedImageProvider"
    android:authorities="com.android.gallery3d.filtershow.provider.SharedImageProvider"
    android:grantUriPermissions="true"
    android:readPermission="com.android.gallery3d.filtershow.permission.READ"
    android:writePermission="com.android.gallery3d.filtershow.permission.WRITE" />

1.11.1 如果我們只需要開放部份的 URI 給其他的應(yīng)用訪問呢?

如果我們只需要開放部份的 URI 給其他的應(yīng)用訪問呢
  • 可以參考 ProviderURI 權(quán)限設(shè)置奋姿,只允許訪問部份 URI 锄开,可以參考原生 ContactsProvider2 的相關(guān)代碼( 注意 path-permission 這個選項 ):
<provider android:name="ContactsProvider2"
    android:authorities="contacts;com.android.contacts"
    android:label="@string/provider_label"
    android:multiprocess="false"
    android:exported="true"
    android:grantUriPermissions="true"
    android:readPermission="android.permission.READ_CONTACTS"
    android:writePermission="android.permission.WRITE_CONTACTS">
    <path-permission
            android:pathPrefix="/search_suggest_query"
            android:readPermission="android.permission.GLOBAL_SEARCH" />
    <path-permission
            android:pathPrefix="/search_suggest_shortcut"
            android:readPermission="android.permission.GLOBAL_SEARCH" />
    <path-permission
            android:pathPattern="/contacts/.*/photo"
            android:readPermission="android.permission.GLOBAL_SEARCH" />
    <grant-uri-permission android:pathPattern=".*" />
</provider>

1.12 ContentProvider 接口方法運行在哪個線程中呢胀蛮?

ContentProvider 接口方法運行在哪個線程中
  • ContentProvider 可以在 AndroidManifest.xml 中配置一個叫做 android:multiprocess 的屬性院刁,默認值是 false ,表示 ContentProvider 是單例的
  • 無論哪個客戶端應(yīng)用的訪問都將是同一個 ContentProvider 對象粪狼,如果設(shè)為 true 退腥,系統(tǒng)會為每一個訪問該 ContentProvider 的進程創(chuàng)建一個實例任岸。

1.12.1 這點還是比較好理解的,那如果我要問每個 ContentProvider 的操作是在哪個線程中運行的呢?( 其實我們關(guān)心的是 UI 線程和工作線程 )

每個 ContentProvider 的操作是在哪個線程中運行的
  • 比如我們在UI線程調(diào)用getContentResolver().query查詢數(shù)據(jù)狡刘,而當(dāng)數(shù)據(jù)量很大時(或者需要進行較長時間的計算)會不會阻塞UI線程呢享潜?

  • 要分兩種情況回答這個問題:

  1. ContentProvider 和調(diào)用者在同一個進程,ContentProvider 的方法( query/insert/update/delete 等 )和調(diào)用者在同一線程中嗅蔬;
  2. ContentProvider 和調(diào)用者在不同的進程剑按,ContentProvider 的方法會運行在它自身所在進程的一個 Binder 線程中。
    但是澜术,注意這兩種方式在 ContentProvider 的方法沒有執(zhí)行完成前都會 blocked 調(diào)用者艺蝴。所以你應(yīng)該知道這個上面這個問題的答案了吧。
  3. 也可以看看 CursorLoader 這個類的源碼鸟废,看 Google 自己是怎么使用 getContentResolver().query 的猜敢。

1.13 ContentProvider 是如何在不同應(yīng)用程序之間傳輸數(shù)據(jù)的?

ContentProvider 是如何在不同應(yīng)用程序之間傳輸數(shù)據(jù)
  • 一個應(yīng)用進程有 16Binder 線程去和遠程服務(wù)進行交互盒延,而每個線程可占用的緩存空間是 128KB 這樣缩擂,超過會報異常。
  • ContentResolver 雖然是通過 Binder 進程間通信機制打通了應(yīng)用程序之間共享數(shù)據(jù)的通道添寺,但 ContentProvider 組件在不同應(yīng)用程序之間傳輸數(shù)據(jù)是基于匿名共享內(nèi)存機制來實現(xiàn)的胯盯。
  • 有興趣的可以查看一下老羅的文章Android系統(tǒng)匿名共享內(nèi)存Ashmem(Anonymous Shared Memory)簡要介紹和學(xué)習(xí)計劃

ContentProvider

總結(jié)


  1. 在這篇文章中计露,我對我所知道的 BroadcastReceiver 知識總進行了詳細的總結(jié)博脑,希望大家通過本次閱讀都能有所收獲。
  2. 重點:學(xué) Android 有一段時間了票罐,我打算好好的梳理一下所學(xué)知識趋厉,到現(xiàn)在為止,我才總結(jié)完 Activity 胶坠、ServiceBroadcastRecevier 等繁堡,有關(guān) 事件分發(fā)沈善、滑動沖突、新能優(yōu)化等重要模塊椭蹄,我后面也將詳盡的總結(jié)闻牡,歡迎大家關(guān)注 _yuanhao 的簡書~ ,方便及時接收更新 绳矩,方便及時接收更新
  3. 如果有可以補充的知識點罩润,歡迎大家在評論區(qū)指出。

碼字不易翼馆,你的點贊是我總結(jié)的最大動力割以!


Android
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市桦卒,隨后出現(xiàn)的幾起案子立美,更是在濱河造成了極大的恐慌,老刑警劉巖方灾,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件建蹄,死亡現(xiàn)場離奇詭異,居然都是意外死亡裕偿,警方通過查閱死者的電腦和手機洞慎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嘿棘,“玉大人劲腿,你說我怎么就攤上這事∧衩睿” “怎么了焦人?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長重父。 經(jīng)常有香客問我花椭,道長,這世上最難降的妖魔是什么房午? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任矿辽,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘袋倔。我一直安慰自己雕蔽,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布奕污。 她就那樣靜靜地躺著萎羔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪碳默。 梳的紋絲不亂的頭發(fā)上贾陷,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音嘱根,去河邊找鬼髓废。 笑死,一個胖子當(dāng)著我的面吹牛该抒,可吹牛的內(nèi)容都是我干的慌洪。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼凑保,長吁一口氣:“原來是場噩夢啊……” “哼冈爹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起欧引,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤频伤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后芝此,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體憋肖,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年婚苹,在試婚紗的時候發(fā)現(xiàn)自己被綠了岸更。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡膊升,死狀恐怖怎炊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情廓译,我是刑警寧澤结胀,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站责循,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏攀操。R本人自食惡果不足惜院仿,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧歹垫,春花似錦剥汤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至暮芭,卻和暖如春鹿驼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辕宏。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工畜晰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瑞筐。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓凄鼻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親聚假。 傳聞我的和親對象是個殘疾皇子块蚌,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

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