一、前言
在 Framework 源碼解析知識(shí)梳理(5) - startService 源碼分析 中,我們分析了Service
啟動(dòng)的內(nèi)部實(shí)現(xiàn)原理,今天,我們趁熱打鐵,看一下Android
中的四大組件中另一個(gè)組件ContentProvider
。
二、源碼解析
在分析之前,先上一張整個(gè)的流程圖,大家在后面繞暈了以后,可以參考這張圖進(jìn)行對(duì)照:
2.1 ContentResolver 獲取過(guò)程
在使用ContentProvider
來(lái)進(jìn)行數(shù)據(jù)的增刪改查時(shí),第一步就是要通過(guò)getContentResolver()
,獲得一個(gè)ContentResolver
對(duì)象,該方法實(shí)際上調(diào)用了基類中的mBase
變量,也就是ContextImpl
中的getContentResolver()
方法,并返回它其中的mContentResolver
變量。
//ContextImpl.java
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
而該mContentResolver
是在ContextImpl
的構(gòu)造函數(shù)中初始化的,這其實(shí)和我們之前在 插件化知識(shí)梳理(9) - 資源的動(dòng)態(tài)加載示例及源碼分析 中所分析的getResources()
方法返回一個(gè)Resources
對(duì)象的過(guò)程類似。
private ContextImpl(ContextImpl container, ActivityThread mainThread,
LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
Display display, Configuration overrideConfiguration, int createDisplayWithId) {
//...
mContentResolver = new ApplicationContentResolver(this, mainThread, user);
}
這上面的ApplicationContentResolver
是ContentResolver
的子類:
2.2 簡(jiǎn)單的查詢過(guò)程
現(xiàn)在,我們以ContentResolver
所提供的query
方法為例,對(duì)ContentProvider
的調(diào)用過(guò)程進(jìn)行一次簡(jiǎn)單的走讀:
public final @Nullable Cursor query(final @NonNull Uri uri, @Nullable String[] projection,
@Nullable String selection, @Nullable String[] selectionArgs,
@Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
Preconditions.checkNotNull(uri, "uri");
//1.獲取ContentProvider接口。
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
long startTime = SystemClock.uptimeMillis();
ICancellationSignal remoteCancellationSignal = null;
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
//2.創(chuàng)建取消信號(hào)量。
remoteCancellationSignal = unstableProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
try {
//3.調(diào)用IContentProvider的query方法。
qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
} catch (DeadObjectException e) {
//如果發(fā)生了異常,那么銷毀unstableProvider對(duì)象,重新獲取一個(gè)stableProvider對(duì)象。
unstableProviderDied(unstableProvider);
stableProvider = acquireProvider(uri);
//如果stableProvider對(duì)象還是為空,那么直接返回空。
if (stableProvider == null) {
return null;
}
//調(diào)用stableProvider進(jìn)行查詢。
qCursor = stableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
}
if (qCursor == null) {
return null;
}
// Force query execution. Might fail and throw a runtime exception here.
qCursor.getCount();
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
//用CursorWrapperInner把qCursor包裹起來(lái)。
CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
stableProvider != null ? stableProvider : acquireProvider(uri));
stableProvider = null;
qCursor = null;
return wrapper;
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
} finally {
if (qCursor != null) {
qCursor.close();
}
if (cancellationSignal != null) {
cancellationSignal.setRemote(null);
}
if (unstableProvider != null) {
releaseUnstableProvider(unstableProvider);
}
if (stableProvider != null) {
releaseProvider(stableProvider);
}
}
}
我們對(duì)上面的流程進(jìn)行一個(gè)簡(jiǎn)單的梳理:
- 通過(guò)
acquireUnstableProvider
獲取一個(gè)unstableProvider
實(shí)例,按字面上的翻譯它是一個(gè)不穩(wěn)定的ContentProvider
。 - 通過(guò)第一步中獲取的
unstableProvider
實(shí)例進(jìn)行查詢,如果查詢成功,那么得到qCursor
對(duì)象;如果ContentProvider
所對(duì)應(yīng)的進(jìn)程已經(jīng)死亡,那么將會(huì)釋放unstableProvider
對(duì)象,再通過(guò)調(diào)用acquireProvider
方法重新得到一個(gè)stableProvider
,它和unstableProvider
相同,都是實(shí)現(xiàn)了IContentProvider
接口,之后在通過(guò)它來(lái)查詢得到qCursor
。 - 把第二步中獲得的
qCursor
用CursorWrapperInner
包裹起來(lái),這里需要注意的是第二個(gè)參數(shù),如果是通過(guò)unstableProvider
查詢得到的qCursor
,那么將需要調(diào)用acquireProvider
,并將返回值傳入。
那么,我們接下來(lái)就要分析通過(guò)acquireUnstableProvider
、acquireProvider
獲取IContentProvider
的過(guò)程。
2.3 IContentProvider 獲取過(guò)程
首先,通過(guò)acquireUnstableProvider
方法根據(jù)Uri
中的authority
字段,調(diào)用acquireUnstableProvider(Context c, String auth)
方法:
該方法是由我們前面看到的
ApplicationContentResolver
所實(shí)現(xiàn)的:可以看到,這里調(diào)用了
mMainThread
的acquireProvider
方法,它實(shí)際上是一個(gè)ActivityThread
實(shí)例,其實(shí)現(xiàn)為:
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
//首先從緩存中獲取,如果獲取到就直接返回。
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
IActivityManager.ContentProviderHolder holder = null;
try {
//如果緩存當(dāng)中沒(méi)有,那么首先通過(guò)AMS進(jìn)行獲取。
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
}
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth);
return null;
}
//根據(jù)返回的holder信息進(jìn)行安裝。
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
這里,首先會(huì)去緩存中查找IContentProvider
,如果沒(méi)有找到,那么在調(diào)用AMS
的方法去查找,獲取一個(gè)ContentProviderHolder
對(duì)象。
2.3.1 調(diào)用者進(jìn)程不存在緩存的情況
在這種情況下面,會(huì)執(zhí)行兩步操作:
- 第一步:通過(guò)
ActivityManagerService
獲取ContentProviderHolder
- 第二步:通過(guò)返回的
ContentProviderHolder
中的信息進(jìn)行安裝
第一步,通過(guò) ActivityManagerService 獲取 ContentProviderHolder
這里我們先假設(shè)沒(méi)有緩存的情況,通過(guò) Framework 源碼解析知識(shí)梳理(1) - 應(yīng)用進(jìn)程與 AMS 的通信實(shí)現(xiàn) 中學(xué)到的知識(shí),我們知道它最終會(huì)調(diào)用到ActivityManagerService
的下面這個(gè)方法:
接下來(lái)最終會(huì)調(diào)用到
getContentProviderImpl
方法返回一個(gè)ContentProviderHolder
對(duì)象,這個(gè)方法比較長(zhǎng),就不貼代碼了,直接說(shuō)結(jié)論,這里會(huì)分為以下幾種情況:
(a) ContentProvider 所在進(jìn)程已經(jīng)啟動(dòng),并且已經(jīng)該 ContentProvider 已經(jīng)被安裝
這種情況下,直接返回該ContentProviderHolder
即可:
(b) ContentProvider 所在進(jìn)程已經(jīng)啟動(dòng),但是該 ContentProvider 沒(méi)有被安裝
此時(shí),就需要通過(guò)ApplicationThread
對(duì)象,再和ContentProvider
所在的進(jìn)程進(jìn)行交互,以返回一個(gè)ContentProviderHolder
實(shí)例:
經(jīng)過(guò)
Binder
通信,那么最終會(huì)調(diào)用到ContentProvider
所在進(jìn)程的下面這個(gè)方法:這里面調(diào)用有調(diào)用了內(nèi)部的
installContentProviders
方法:這里的操作分為兩步:
- 安裝:根據(jù)傳過(guò)來(lái)的
List<ProviderInfo>
對(duì)象,通過(guò)installProvider
方法進(jìn)行安裝,并將結(jié)果存放在List<ContentProviderHolder>
列表中。 - 發(fā)布:將安裝的結(jié)果,再通過(guò)一次消息傳遞,返回給
ActivityManagerService
。
(b-1) 安裝過(guò)程
在這一步當(dāng)中,傳入的第二個(gè)參數(shù)holder
為null
,因此會(huì)根據(jù)Provider
的名字,動(dòng)態(tài)地加載該類,并調(diào)用它的attachInfo
方法:
我們上面的有兩個(gè)
Provider
:
-
localProvider
,類型為ContentProvider
-
provider
,類型為Transport
provider
是通過(guò)localProvider
的getIContentProvider
方法獲得的,它是ContentProvider
的一個(gè)內(nèi)部類,它的作用就是作為ContentProvider
在遠(yuǎn)程調(diào)用者中的一個(gè)代理對(duì)象,也就是說(shuō),ContentProvider
的使用者是通過(guò)獲取ContentProvider
所在進(jìn)程的一個(gè)代理類Transport
,再通過(guò)這個(gè)Transport
對(duì)象調(diào)用到ContentProvider
進(jìn)行查詢的:
接下來(lái),還會(huì)去調(diào)用
localProvider
的attachInfo
方法,這里面會(huì)初始化權(quán)限相關(guān)的信息,最終會(huì)執(zhí)行ContentProvider
的onCreate()
方法:假設(shè)上面我們獲得的
localProvider
不為空,那么會(huì)執(zhí)行下面的邏輯:這里面,我們會(huì)生成一個(gè)
ProviderClientRecord
對(duì)象,其內(nèi)部包含了下面幾個(gè)變量:-
mNames
:ContentProvider
對(duì)象的authority
-
mProvider
:遠(yuǎn)程代理對(duì)象 -
mLocalProvider
:本地對(duì)象 -
mHolder
:返回給AMS
的數(shù)據(jù)結(jié)構(gòu),AMS
再會(huì)把它返回給ContentProvider
的調(diào)用者,mHolder
的類型為IActivityManager.ContentProviderHolder
,其內(nèi)部包含的數(shù)據(jù)結(jié)構(gòu)為:
關(guān)于ContentProviderHolder
和ProviderClientRecord
,其繼承族譜如下圖所示:
(b-2) 發(fā)布過(guò)程
發(fā)布過(guò)程,其實(shí)就是調(diào)用了ActivityManagerService
的publishContentProviders
方法,將在ContentProvider
擁有者所創(chuàng)建的List<ContentProviderHolder>
保存起來(lái):
(c) ContentProvider 所在進(jìn)程沒(méi)有啟動(dòng)
在這種情況下,就需要先通過(guò)startProcessLocked
啟動(dòng)ContentProvider
所在進(jìn)程,等待進(jìn)程啟動(dòng)完畢之后,再進(jìn)行安裝。
第二步,利用返回的 ContentProviderHolder 中的信息,進(jìn)行安裝
在第一步中,通過(guò)ActivityManagerService
,我們最終獲得了ContentProviderHolder
對(duì)象,接下來(lái)就是調(diào)用installProvider
方法,這里和我們之前在第一步中的(b-1)
中所看到的installProvider
其實(shí)是同一個(gè)方法,區(qū)別在于,之前我們分析的installProvider
傳入的holder
參數(shù)為空,下面,我們就來(lái)看一下當(dāng)holder
參數(shù)不為空時(shí)最終會(huì)走到下面的邏輯:
在
installProviderAuthoritiesLocked
方法中,會(huì)將它緩存在mProviderMap
當(dāng)中。2.3.2 調(diào)用者進(jìn)程存在緩存的情況
當(dāng)調(diào)用者進(jìn)程存在緩存時(shí),會(huì)調(diào)用acquireExistingProvider
方法,這里面就會(huì)通過(guò)我們前面所看到的mProviderMap
進(jìn)行查找:
三、小結(jié)
這篇文章拖了一個(gè)星期,總算是完成了,源碼看的真的頭暈,其實(shí)最終看下來(lái),發(fā)現(xiàn)整個(gè)調(diào)用過(guò)程,和我們之前分析過(guò)的 Framework 源碼解析知識(shí)梳理(5) - startService 源碼分析 很類似,究其根本,就是調(diào)用者進(jìn)程、所有者進(jìn)程和ActivityManagerService
進(jìn)程的三方調(diào)用。
更多文章,歡迎訪問(wèn)我的 Android 知識(shí)梳理系列:
- Android 知識(shí)梳理目錄:http://www.lxweimin.com/p/fd82d18994ce
- 個(gè)人主頁(yè):http://lizejun.cn
- 個(gè)人知識(shí)總結(jié)目錄:http://lizejun.cn/categories/