準(zhǔn)備
-
基本使用
ContentProvider作為四大組件之一坞琴,在開發(fā)過程中經(jīng)常被使用到翎卓。我們的常規(guī)做法是定義一個ContentProvider埋心,然后在使用的時候使用ContentResolver提供的接口來訪問數(shù)據(jù)。一個進(jìn)程可以訪問自己定義的ContentProvider韭赘,也可以訪問其他進(jìn)程定義的ContentProvider筒捺。 -
ContentProviderHolder:
ContentProviderHolder是定義在IActivityManager類中的內(nèi)部類,它的作用主要是承載ContentProvider的信息和ContentProvider接口內(nèi)容纸厉,并在SystemServer和App進(jìn)程間傳遞系吭。其主要成員變量如下:- info :ProviderInfo 描述該ContentProvider的信息。
- provider :IContentProvider 描述該ContentProvider對應(yīng)的ContentProvider接口颗品,它是一個Binder代理對象肯尺。
-
大致流程:假設(shè)App B定義了一個ContentProvider,App A需要訪問該ContentProvider躯枢。整個調(diào)用過程簡單梳理會經(jīng)過以下幾個步驟:
- App A先向SystemServer查詢要訪問的ContentProvider接口则吟。
- SystemServer將查詢到的ContentProvider接口返回給App ,該ContentProvider接口是定義在App B中的ContentProvider的Binder代理锄蹂。
- App A再通過該Binder代理調(diào)用定義在App B中的ContentProvider的邏輯氓仲。
- 本地緩存:ActivityThread內(nèi)部會緩存當(dāng)前進(jìn)程已經(jīng)訪問過的ContentProvider或者是當(dāng)前進(jìn)程內(nèi)自己定義的ContentProvider。緩存在其成員變量mProviderMap中得糜。這里注意參考上一篇一圖解惑對比區(qū)分敬扛,在AMS中也有個同名的成員變量mProviderMap,可以大致理解為他們都用來緩存ContentProvider接口朝抖,只不過一個是在本地進(jìn)程中緩存啥箭,一個是在SystemServer中全局緩存。
上圖
這次不墨跡治宣,先上圖再說:
圖中每個步驟都標(biāo)了序號急侥,整個過程涉及到3個進(jìn)程間的通信砌滞。App A是ContentProvider的使用方,App B是ContentProvider的定義方坏怪,SystemServer是系統(tǒng)進(jìn)程贝润。下面就按序號順序做簡要說明。
簡要總結(jié):
1.業(yè)務(wù)邏輯層通過直接使用ContentResolver提供的接口去訪問數(shù)據(jù)(如query陕悬,insert等等)题暖。
2.ContentResolver需要獲取到ContentProvider接口才能對該ContentProvider訪問,所以這里ContentResolver先通過ActivityThread去獲取一個ContentProvider接口捉超。
3.上面提到ActivityThread的mProviderMap成員會緩存已經(jīng)獲取的ContentProvider接口或定義在自己進(jìn)程內(nèi)的ContentProvider接口胧卤。所以在這里ActivityThread會先通過acquireExistingProvider()方法在本地查找是否已有緩存。如果有拼岳,則直接跳到第13步返回結(jié)果給ContentResolver枝誊。如果沒有則會繼續(xù)通過進(jìn)程間通信向SystemServer中的AMS模塊查詢ContentProvider接口。
4.向AMS查詢ContentProvider接口惜纸。在AMS的mProviderMap成員中保存了當(dāng)前運(yùn)行的ContentProvider接口叶撒,查詢過程中大方向會有兩個分支:如果要查詢的ContentProvider接口不在mProviderMap緩存中,則一般情況下也說明該ContentProviders所在的進(jìn)程還沒啟動耐版,則繼續(xù)往下執(zhí)行第5步啟動該進(jìn)程祠够。否則直接跳到第12步,將從mProviderMap成員中查詢到的結(jié)果返回給ActivityThread粪牲。
5.啟動要查找的ContentProvider所在的進(jìn)程古瓤。接下來便是大家熟知的應(yīng)用進(jìn)程啟動的過程。這里主要關(guān)注第10步和第11步腺阳。
6~9.應(yīng)用進(jìn)程啟動初始化過程中與AMS的一系列交互落君。
10.通過調(diào)用installContentProviders()方法本地安裝ContentProvider,這里可以簡單理解所謂本地安裝就是填充ActivityThread中保存ContentProvider接口的緩存亭引,但是要注意當(dāng)前的ActivityThread是表示定義ContentProvider的進(jìn)程(App B)绎速,而不是使用ContentProvider的進(jìn)程(App A)。
11.回調(diào)AMS的publishContentProviders()方法焙蚓,將ContentProvider接口傳給AMS纹冤。至此AMS內(nèi)部的mProviderMap成員也獲取到了ContentProvider接口并保存。當(dāng)下次有進(jìn)程再請求訪問同一個ContentProvider接口時购公,就可以直接從AMS的mProviderMap成員中獲取該ContentProvider接口直接返回給調(diào)用方了赵哲。
12.返回查詢結(jié)果給ContentProvider的使用方(App A)缴守。這里要注意返回的數(shù)據(jù)類型是ContentProviderHolder眷茁,它繼承了Parcelable接口。因此可以在進(jìn)程間傳遞雏婶,使用方需要的ContentProvider接口就保存在其成員變量provider中绘闷。
13.ContentProvider的使用方將查找回來的ContentProvider接口返回給ContentResolver橡庞。
14.ContentResolver通過ContentProvider接口再次發(fā)起B(yǎng)inder通信向真正ContentProvider的實現(xiàn)方(App B)發(fā)送數(shù)據(jù)訪問請求较坛。
15.ContentProvider實現(xiàn)方(App B)將數(shù)據(jù)返回給Content使用方(App A)。
16.ContentResolver完成數(shù)據(jù)的訪問工作扒最,將查詢結(jié)果返回給業(yè)務(wù)邏輯層丑勤。
整個過程有可能只涉及一個進(jìn)程(App A),比如以下幾種情況:
- ContentProvider的使用方(App A)已經(jīng)有要訪問的ContentProvider接口的緩存吧趣。
- ContentProvider的定義與使用在相同的進(jìn)程法竞。
也可能涉及兩個進(jìn)程(App A和SystemServer):
- ContentProvider的定義與使用在不同的進(jìn)程,但是AMS中已經(jīng)有該ContentProvider接口的緩存强挫。
也可能涉及三個進(jìn)程(App A岔霸,App B和SystemServer):
- ContentProvider的定義與使用在不同的進(jìn)程,并且AMS中也沒有該ContentProvider接口的緩存俯渤。
And Then
我的分析默認(rèn)是按照Android N原代碼的邏輯來分析的呆细。如果在國產(chǎn)機(jī)上發(fā)現(xiàn)邏輯與之有沖突不要驚慌,國產(chǎn)機(jī)ROM在AOSP基礎(chǔ)上做了很多“優(yōu)化”八匠⌒跻或者不同的Android版本之間可能也存在微小差別。比如我的測試過程中就發(fā)現(xiàn)手上一臺華為在App B定義了ContentProvider梨树,但是App B進(jìn)程還沒有啟動時坑夯,App A使用該ContentProvider并不會把App B拉起,而是拋一個Warning:Unknown URL content://xxxxxxxxxxxxx抡四。
四大組件中之所以沒有先寫Activity和Broadcast是因為網(wǎng)上他們哥兩的介紹相對多的多柜蜈。不過后面我也會抽空把一圖解惑系列的四大組件都補(bǔ)全。一圖解惑系列的目的是希望盡量簡單直接的將基本的原理邏輯說清楚床嫌,如果需要了解內(nèi)部細(xì)節(jié),當(dāng)然還是需要Read The Fucking Source Code胸私。
Thank you~