一、前言
在 Framework 源碼解析知識梳理(5) - startService 源碼分析 中,我們分析了Service啟動的內(nèi)部實現(xiàn)原理,今天,我們趁熱打鐵,看一下Android中的四大組件中另一個組件ContentProvider。
二、源碼解析
在分析之前,先上一張整個的流程圖,大家在后面繞暈了以后,可以參考這張圖進行對照:

2.1 ContentResolver 獲取過程
在使用ContentProvider來進行數(shù)據(jù)的增刪改查時,第一步就是要通過getContentResolver(),獲得一個ContentResolver對象,該方法實際上調(diào)用了基類中的mBase變量,也就是ContextImpl中的getContentResolver()方法,并返回它其中的mContentResolver變量。
//ContextImpl.java
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
而該mContentResolver是在ContextImpl的構造函數(shù)中初始化的,這其實和我們之前在 插件化知識梳理(9) - 資源的動態(tài)加載示例及源碼分析 中所分析的getResources()方法返回一個Resources對象的過程類似。
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 簡單的查詢過程
現(xiàn)在,我們以ContentResolver所提供的query方法為例,對ContentProvider的調(diào)用過程進行一次簡單的走讀:
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)建取消信號量。
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對象,重新獲取一個stableProvider對象。
unstableProviderDied(unstableProvider);
stableProvider = acquireProvider(uri);
//如果stableProvider對象還是為空,那么直接返回空。
if (stableProvider == null) {
return null;
}
//調(diào)用stableProvider進行查詢。
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包裹起來。
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);
}
}
}
我們對上面的流程進行一個簡單的梳理:
- 通過
acquireUnstableProvider獲取一個unstableProvider實例,按字面上的翻譯它是一個不穩(wěn)定的ContentProvider。 - 通過第一步中獲取的
unstableProvider實例進行查詢,如果查詢成功,那么得到qCursor對象;如果ContentProvider所對應的進程已經(jīng)死亡,那么將會釋放unstableProvider對象,再通過調(diào)用acquireProvider方法重新得到一個stableProvider,它和unstableProvider相同,都是實現(xiàn)了IContentProvider接口,之后在通過它來查詢得到qCursor。 - 把第二步中獲得的
qCursor用CursorWrapperInner包裹起來,這里需要注意的是第二個參數(shù),如果是通過unstableProvider查詢得到的qCursor,那么將需要調(diào)用acquireProvider,并將返回值傳入。
那么,我們接下來就要分析通過acquireUnstableProvider、acquireProvider獲取IContentProvider的過程。
2.3 IContentProvider 獲取過程
首先,通過acquireUnstableProvider方法根據(jù)Uri中的authority字段,調(diào)用acquireUnstableProvider(Context c, String auth)方法:

該方法是由我們前面看到的
ApplicationContentResolver所實現(xiàn)的:
可以看到,這里調(diào)用了
mMainThread的acquireProvider方法,它實際上是一個ActivityThread實例,其實現(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 {
//如果緩存當中沒有,那么首先通過AMS進行獲取。
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信息進行安裝。
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
這里,首先會去緩存中查找IContentProvider,如果沒有找到,那么在調(diào)用AMS的方法去查找,獲取一個ContentProviderHolder對象。
2.3.1 調(diào)用者進程不存在緩存的情況
在這種情況下面,會執(zhí)行兩步操作:
- 第一步:通過
ActivityManagerService獲取ContentProviderHolder - 第二步:通過返回的
ContentProviderHolder中的信息進行安裝
第一步,通過 ActivityManagerService 獲取 ContentProviderHolder
這里我們先假設沒有緩存的情況,通過 Framework 源碼解析知識梳理(1) - 應用進程與 AMS 的通信實現(xiàn) 中學到的知識,我們知道它最終會調(diào)用到ActivityManagerService的下面這個方法:

接下來最終會調(diào)用到
getContentProviderImpl方法返回一個ContentProviderHolder對象,這個方法比較長,就不貼代碼了,直接說結論,這里會分為以下幾種情況:
(a) ContentProvider 所在進程已經(jīng)啟動,并且已經(jīng)該 ContentProvider 已經(jīng)被安裝
這種情況下,直接返回該ContentProviderHolder即可:

(b) ContentProvider 所在進程已經(jīng)啟動,但是該 ContentProvider 沒有被安裝
此時,就需要通過ApplicationThread對象,再和ContentProvider所在的進程進行交互,以返回一個ContentProviderHolder實例:

經(jīng)過
Binder通信,那么最終會調(diào)用到ContentProvider所在進程的下面這個方法:
這里面調(diào)用有調(diào)用了內(nèi)部的
installContentProviders方法:
這里的操作分為兩步:
- 安裝:根據(jù)傳過來的
List<ProviderInfo>對象,通過installProvider方法進行安裝,并將結果存放在List<ContentProviderHolder>列表中。 - 發(fā)布:將安裝的結果,再通過一次消息傳遞,返回給
ActivityManagerService。
(b-1) 安裝過程
在這一步當中,傳入的第二個參數(shù)holder為null,因此會根據(jù)Provider的名字,動態(tài)地加載該類,并調(diào)用它的attachInfo方法:

我們上面的有兩個
Provider:
-
localProvider,類型為ContentProvider -
provider,類型為Transport
provider是通過localProvider的getIContentProvider方法獲得的,它是ContentProvider的一個內(nèi)部類,它的作用就是作為ContentProvider在遠程調(diào)用者中的一個代理對象,也就是說,ContentProvider的使用者是通過獲取ContentProvider所在進程的一個代理類Transport,再通過這個Transport對象調(diào)用到ContentProvider進行查詢的:

接下來,還會去調(diào)用
localProvider的attachInfo方法,這里面會初始化權限相關的信息,最終會執(zhí)行ContentProvider的onCreate()方法:
假設上面我們獲得的
localProvider不為空,那么會執(zhí)行下面的邏輯:
這里面,我們會生成一個
ProviderClientRecord對象,其內(nèi)部包含了下面幾個變量:
-
mNames:ContentProvider對象的authority -
mProvider:遠程代理對象 -
mLocalProvider:本地對象 -
mHolder:返回給AMS的數(shù)據(jù)結構,AMS再會把它返回給ContentProvider的調(diào)用者,mHolder的類型為IActivityManager.ContentProviderHolder,其內(nèi)部包含的數(shù)據(jù)結構為:
關于ContentProviderHolder和ProviderClientRecord,其繼承族譜如下圖所示:

(b-2) 發(fā)布過程
發(fā)布過程,其實就是調(diào)用了ActivityManagerService的publishContentProviders方法,將在ContentProvider擁有者所創(chuàng)建的List<ContentProviderHolder>保存起來:

(c) ContentProvider 所在進程沒有啟動
在這種情況下,就需要先通過startProcessLocked啟動ContentProvider所在進程,等待進程啟動完畢之后,再進行安裝。

第二步,利用返回的 ContentProviderHolder 中的信息,進行安裝
在第一步中,通過ActivityManagerService,我們最終獲得了ContentProviderHolder對象,接下來就是調(diào)用installProvider方法,這里和我們之前在第一步中的(b-1)中所看到的installProvider其實是同一個方法,區(qū)別在于,之前我們分析的installProvider傳入的holder參數(shù)為空,下面,我們就來看一下當holder參數(shù)不為空時最終會走到下面的邏輯:

在
installProviderAuthoritiesLocked方法中,會將它緩存在mProviderMap當中。
2.3.2 調(diào)用者進程存在緩存的情況
當調(diào)用者進程存在緩存時,會調(diào)用acquireExistingProvider方法,這里面就會通過我們前面所看到的mProviderMap進行查找:

三、小結
這篇文章拖了一個星期,總算是完成了,源碼看的真的頭暈,其實最終看下來,發(fā)現(xiàn)整個調(diào)用過程,和我們之前分析過的 Framework 源碼解析知識梳理(5) - startService 源碼分析 很類似,究其根本,就是調(diào)用者進程、所有者進程和ActivityManagerService進程的三方調(diào)用。
更多文章,歡迎訪問我的 Android 知識梳理系列:
- Android 知識梳理目錄:http://www.itdecent.cn/p/fd82d18994ce
- 個人主頁:http://lizejun.cn
- 個人知識總結目錄:http://lizejun.cn/categories/
