Android ContentProvider原理分析
目錄
- ContentProvider概述
- 類圖
- 時序圖
- 原始碼解析
- installProvider
- ContentResolver中的CURD
- acquireProvider
- 到AMS獲取ContentProvider
- publishContentProvider
- removeDyingProvider
- 總結
1. ContentProvider概述
ContentProvider作為Android四大元件之一,重要性肯定是不言而喻的,顧名思義,內容提供者,其最重要的作用就在於提供了一種跨程序獲取資料的方式,provider元件不僅可以自己的程序使用,還可以提供給其他的程序使用,大大方便了不同程序之間的資料交換,本文將詳細介紹provider執行的原理。 注
2. 類圖
看起來涉及到的類非常多,一下有種不知道從何看起的感覺,所以這裡對於其中的重點關注部分加上了顏色:
白色:provider執行過程中涉及到的內部類或私有類,一般APP開發過程中不太會涉及
藍色:APP開發過程中經常會接觸到的類
紫色:在system server
程序中provider相關的類
3. 時序圖
其中白色部分:發起獲取provider的client程序 紅色部分:systemserver程序 藍色部分:提供provider的server程序 時序圖解讀:
- 1~4流程是在APP自己的程序(程序A)中,一般來講APP在程序啟動之後初始化時,就會installProvider(流程7~10),如果APP請求的provider在自己程序,那麼到4就能獲取到。
- 如果請求的provider另外一個程序(程序B)中,則會觸發程序5~6
- 如果程序B不存在則先啟動程序B並installprovider(7~10),告訴AMS之後,由AMS返回給程序A對方的provider資訊(此過程中由程序A發起的請求provider的執行緒會一直等待)
- 如果程序B存在則AMS直接返回給程序A對方的provider資訊
- 查詢到provider資訊之後,如果需要跨程序呼叫,則通過ContentProviderProxy發起binder call到對端程序執行query 在這其中,AMS充當一箇中間管理員的角色,每個程序在啟動之後需要把自己應該install的providerinstall之後告訴AMS,這樣後面有其他程序請求這個provider的話,AMS可以告訴你所請求的對端的資訊。
4. 原始碼解析
4.1 ActivityThread.installProvider
installProvider,顧名思義就是安裝provider,說的通俗一點就是把APP程序中的provider元件封裝成物件儲存起來,方便使用 在APP的程序啟動的時候,handleBindApplication中會觸發installProvider:
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
// 此處的provider資訊是在AMS啟動程序時
// 從manifest收集到的需要install的provider資訊
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
for (ProviderInfo cpi : providers) {
// 執行installProvider,注意此處的stable引數預設為true
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
// install完成之後,要告訴AMS
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
進一步看一下ActivityThread.installProvider的具體實現
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
// holder為null表示還沒有install過
if (holder == null || holder.provider == null) {
Context c = null;
ApplicationInfo ai = info.applicationInfo;
if (context.getPackageName().equals(ai.packageName)) {
c = context;
} else if (mInitialApplication != null &&
mInitialApplication.getPackageName().equals(ai.packageName)) {
c = mInitialApplication;
} else {
try {
// 建立context
c = context.createPackageContext(ai.packageName,
Context.CONTEXT_INCLUDE_CODE);
} catch (PackageManager.NameNotFoundException e) {
// Ignore
}
}
...
try {
final java.lang.ClassLoader cl = c.getClassLoader();
// 通過反射建立provider物件
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
// 獲取IContentProvider物件,用於跨程序binder call
provider = localProvider.getIContentProvider();
if (provider == null) {
Slog.e(TAG, "Failed to instantiate class " +
info.name + " from sourceDir " +
info.applicationInfo.sourceDir);
return null;
}
// provider的attach,其中最重要的是會執行provider的onCreate
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
if (!mInstrumentation.onException(null, e)) {
throw new RuntimeException(
"Unable to get provider " + info.name
+ ": " + e.toString(), e);
}
return null;
}
} else {
provider = holder.provider;
}
// 到這裡,provider的物件建立好了,那麼接下來需要做的就是資料結構的封裝
// 把provider相關資訊儲存起來
ContentProviderHolder retHolder;
// mProviderMap的key時providerKey,value是ProviderClientReocrd
// 這兩個類主要是封裝了一些provider的基本資訊,可以到上面看一下類圖
synchronized (mProviderMap) {
IBinder jBinder = provider.asBinder();
if (localProvider != null) {
ComponentName cname = new ComponentName(info.packageName, info.name);
// mLocalProvidersByName的key是component資訊,value是對應的ProviderClientReocrd
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
if (pr != null) {
// 不為空代表install過
provider = pr.mProvider;
} else {
// 對於新建立的provider,建立其對應的ContentProviderHolder物件
holder = new ContentProviderHolder(info);
holder.provider = provider;
holder.noReleaseNeeded = true;
// install Authorities
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
// mLocalProviders的key是IContentProvider的binder物件,value是ProviderClientRecord
// 將封裝好的provider放入map中
mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
}
retHolder = pr.mHolder;
} else {
// mProviderRefCountMap的key是binder物件,value是ProviderRefCount
// ProviderRefCount中記錄了這個provider的stable和unstable的數量
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
if (!noReleaseNeeded) {
incProviderRefLocked(prc, stable);
try {
ActivityManager.getService().removeContentProvider(
holder.connection, stable);
} catch (RemoteException e) {
}
}
} else {
ProviderClientRecord client = installProviderAuthoritiesLocked(
provider, localProvider, holder);
if (noReleaseNeeded) {
// 建立ProviderRefCount
prc = new ProviderRefCount(holder, client, 1000, 1000);
} else {
// 根據引數中的stable和unstable建立物件
prc = stable
? new ProviderRefCount(holder, client, 1, 0)
: new ProviderRefCount(holder, client, 0, 1);
}
// 放入map
mProviderRefCountMap.put(jBinder, prc);
}
retHolder = prc.holder;
}
}
return retHolder;
}
這裡需要提一下其中的getIContentProvider:
返回的是一個Transport物件,而這又是ContentProviderNative的子類,主要作用就是用來binder通訊的
它的建立:在ContentProvider類中
private Transport mTransport = new Transport();
也就是說在物件建立的時候就預設建立了
然後是ActivityThread.installProviderAuthoritiesLocked
private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
ContentProvider localProvider, ContentProviderHolder holder) {
final String auths[] = holder.info.authority.split(";");
final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);
if (provider != null) {
for (String auth : auths) {
// 對於一些特殊的auth,允許跨程序binder call
// Binder.allowBlocking代表允許執行同步的binder call
switch (auth) {
case ContactsContract.AUTHORITY:
case CallLog.AUTHORITY:
case CallLog.SHADOW_AUTHORITY:
case BlockedNumberContract.AUTHORITY:
case CalendarContract.AUTHORITY:
case Downloads.Impl.AUTHORITY:
case "telephony":
Binder.allowBlocking(provider.asBinder());
}
}
}
// 建立ProviderClientRecord
final ProviderClientRecord pcr = new ProviderClientRecord(
auths, provider, localProvider, holder);
for (String auth : auths) {
// 根據auth和userId建立ProviderKey,放入mProviderMap
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord existing = mProviderMap.get(key);
if (existing != null) {
Slog.w(TAG, "Content provider " + pcr.mHolder.info.name
+ " already published as " + auth);
} else {
mProviderMap.put(key, pcr);
}
}
return pcr;
}
小結:
- 建立了provider物件,其中也建立了IContentProvider物件(Transport)
- 建立ContentProviderHolder
- 建立ProviderKey
- 建立ProviderClientRecord,這是一個provider在client程序中對應的物件
- 分別放入
mProviderMap
/mLocalProviders
/mLocalProvidersByName
- 建立ProviderRefCount,放入
mProviderRefCountMap
這裡先說一下
stable
和unstable
:
- 代表的是client和server的連結,主要取決於獲取provider的傳參,預設情況下,insert/update/delete建立的連結都是stable,而query則是unstable,不過在query的時候如果失敗,還會重新建立stable
- stable和unstable最重大的差別在於unstable的情況下,即使對端掛掉了,client也沒關係,但是stable的話,如果對端程序掛掉了,client也會被跟著級聯kill掉。(後面會介紹)
4.2 ContentResolver中的CURD
4.2.1 ContentResolver.insert
public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
@Nullable ContentValues values) {
Preconditions.checkNotNull(url, "url");
// 請求provider,經過呼叫之後,最終傳參獲取的是stable型別provider
IContentProvider provider = acquireProvider(url);
if (provider == null) {
throw new IllegalArgumentException("Unknown URL " + url);
}
try {
long startTime = SystemClock.uptimeMillis();
// 執行通過IContentProvider執行insert,其實是發起了binder call到了provider所在程序執行
Uri createdRow = provider.insert(mPackageName, url, values);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
return createdRow;
} catch (RemoteException e) {
return null;
} finally {
releaseProvider(provider);
}
}
ContentResolver.delete
:執行provider的delete
ContentResolver.update
:執行provider的update
ContentResolver.call
:執行provider的call,這個比較靈活,傳遞的引數比較多,不固定於某一個
這三個方法的實現都跟instert基本類似,此處不再羅列
下面需要重點說一下query
:
4.2.2 query
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal) {
Preconditions.checkNotNull(uri, "uri");
// 這裡執行的方法是acquireUnstableProvider
// 也就是說建立的連結是unstable型別
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();
remoteCancellationSignal = unstableProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
try {
// 執行query
qCursor = unstableProvider.query(mPackageName, uri, projection,
queryArgs, remoteCancellationSignal);
} catch (DeadObjectException e) {
// 如果query失敗
// 因為unstable型別,失敗代表對端掛掉了,那麼重新請求stable型別連結
unstableProviderDied(unstableProvider);
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
return null;
}
// 再次query
qCursor = stableProvider.query(
mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
}
if (qCursor == null) {
return null;
}
qCursor.getCount();
long durationMillis = SystemClock.uptimeMillis() - startTime;
final IContentProvider provider = (stableProvider != null) ? stableProvider
: acquireProvider(uri);
final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
stableProvider = null;
qCursor = null;
return wrapper;
} catch (RemoteException e) {
...
}
}
- query的時候先建立的unstable連結,然後發起了binder call
- 如果catchRemoteException,那說明對段程序掛了,此時重新請求stable型別連結把對方程序拉起來,再執行query
4.3 ActivityThread.acquireProvider
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;
}
ContentProviderHolder holder = null;
// 如果不存在則需要向AMS查詢
try {
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth);
return null;
}
// 在本程序中installProvider
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
到這裡可能有有一些迷糊,前面已經installProvider過了,為什麼acquireProvider的時候需要再install一次呢? 答案是因為這是兩個不同的程序
- 在bindApplication的時候執行的installProvider是提供provider的程序,也就是server程序
- 而在此處查詢的則是需要獲取provider的程序,也就是client程序
- server程序installProvider的作用是為了自己程序內使用的話在
acquireExistingProvider
的時候就能查到了,不需要在跨程序到AMS去查詢 - client程序installProvider的作用是經過了一次向AMS的查詢之後,客戶端就可以快取起來,這樣就不用每次都向AMS查詢
4.3.1 ActivityThread.acquireExistingProvider
public final IContentProvider acquireExistingProvider(
Context c, String auth, int userId, boolean stable) {
synchronized (mProviderMap) {
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord pr = mProviderMap.get(key);
if (pr == null) {
return null;
}
IContentProvider provider = pr.mProvider;
IBinder jBinder = provider.asBinder();
if (!jBinder.isBinderAlive()) {
// 對端程序掛掉了
handleUnstableProviderDiedLocked(jBinder, true);
return null;
}
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
// 增加引用計數
incProviderRefLocked(prc, stable);
}
return provider;
}
}
4.4 ActivityManagerService.getContentProvider
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name, int userId, boolean stable) {
enforceNotIsolatedCaller("getContentProvider");
if (caller == null) {
String msg = "null IApplicationThread when getting content provider "
+ name;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
return getContentProviderImpl(caller, name, null, stable, userId);
}
簡單對caller進行了判斷,重點在getContentProviderImpl
4.4.1 getContentProviderImpl
方法比較長,刪除了其中一些無用的log以及checkTime
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
synchronized(this) {
// 對caller判斷
boolean checkCrossUser = true;
// 在AMS的providermap中先看看是不是已經存在了
cpr = mProviderMap.getProviderByName(name, userId);
// 如果不存在且system,校驗Singleton,也就是隻能有一個
if (cpr == null && userId != UserHandle.USER_SYSTEM) {
cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
if (cpr != null) {
cpi = cpr.info;
if (isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {
userId = UserHandle.USER_SYSTEM;
checkCrossUser = false;
} else {
cpr = null;
cpi = null;
}
}
}
// 看看這個provider當前是不是已經在運行了
boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;
// 如果已經在運行了,也就說明各個資料結構都已經新增過了,程序也都已經存在了
if (providerRunning) {
cpi = cpr.info;
if (r != null && cpr.canRunHere(r)) {
// canRunHere主要針對自己請求的provider在自己程序中的情況,一般不會遇到
ContentProviderHolder holder = cpr.newHolder(null);
holder.provider = null;
return holder;
}
...
final long origId = Binder.clearCallingIdentity();
// 建立provider之間的連結
conn = incProviderCountLocked(r, cpr, token, stable);
// 需要更新lru佇列
if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
updateLruProcessLocked(cpr.proc, false, null);
}
}
final int verifiedAdj = cpr.proc.verifiedAdj;
// 更新程序優先順序
boolean success = updateOomAdjLocked(cpr.proc, true);
if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) {
success = false;
}
// 記錄usageStates
maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
if (!success) {
// 更新lru失敗代表那個程序掛掉了
boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
// 需要處理程序掛掉的流程
// 並且標誌provider當前不在執行狀態,這樣會走進下面的provider不在執行的流程中
appDiedLocked(cpr.proc);
if (!lastRef) {
return null;
}
providerRunning = false;
conn = null;
} else {
cpr.proc.verifiedAdj = cpr.proc.setAdj;
}
Binder.restoreCallingIdentity(origId);
}
// 如果provider當前不在執行
if (!providerRunning) {
try {
// 先從packageManager那邊查詢到相關的資訊
cpi = AppGlobals.getPackageManager().
resolveContentProvider(name,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
} catch (RemoteException ex) {
}
// 沒查到的話那就說明這個是一個無效的provider
if (cpi == null) {
return null;
}
boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r.uid, cpi.applicationInfo.uid);
if (singleton) {
userId = UserHandle.USER_SYSTEM;
}
// 獲取applicationInfo
cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
String msg;
if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
!= null) {
throw new SecurityException(msg);
}
// 在system啟動之前不允許非system的程序獲取provider
if (!mProcessesReady
&& !cpi.processName.equals("system")) {
throw new IllegalArgumentException(
"Attempt to launch content provider before system ready");
}
// 校驗user是否正在執行中
if (!mUserController.isUserRunningLocked(userId, 0)) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
+ name + ": user " + userId + " is stopped");
return null;
}
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
// 從getProviderByClass中獲取,看看這個provider是不是AMS這邊已經有儲存了
// 如果沒有,代表這個provider從來沒有向AMS註冊過
// 此時需要建立一個新的ContentProviderRecord
cpr = mProviderMap.getProviderByClass(comp, userId);
final boolean firstClass = cpr == null;
if (firstClass) {
final long ident = Binder.clearCallingIdentity();
if (mPermissionReviewRequired) {
if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) {
return null;
}
}
try {
ApplicationInfo ai =
AppGlobals.getPackageManager().
getApplicationInfo(
cpi.applicationInfo.packageName,
STOCK_PM_FLAGS, userId);
if (ai == null) {
Slog.w(TAG, "No package info for content provider "
+ cpi.name);
return null;
}
ai = getAppInfoForUser(ai, userId);
cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
} finally {
Binder.restoreCallingIdentity(ident);
}
}
...
// 看看當前請求的provider是不是在mLaunchingProviders
// 如果一個 provider被請求過,但是因為對方程序沒有啟動沒有publishProvider
// 則會加入mLaunchingProviders中
final int N = mLaunchingProviders.size();
int i;
for (i = 0; i < N; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
// launching中沒有
if (i >= N) {
final long origId = Binder.clearCallingIdentity();
try {
try {
AppGlobals.getPackageManager().setPackageStoppedState(
cpr.appInfo.packageName, false, userId);
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ cpr.appInfo.packageName + ": " + e);
}
// 看看這個程序是不是存在
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null && !proc.killed) {
// 如果程序已經存在,那麼就讓這個程序去installProvider即可
if (!proc.pubProviders.containsKey(cpi.name)) {
proc.pubProviders.put(cpi.name, cpr);
try {
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
} else {
// 如果程序不存在,那就得先啟動程序
// 因為provider必然執行在某一個程序中,對端程序不在肯定無法獲取provider
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
if (proc == null) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
+ name + ": process is bad");
return null;
}
}
// 因為需要啟動程序,記錄這個provider的launchingApp
// 並把這個provider加入到mLaunchingProviders中,等待對方publish之後
// 再從mLaunchingProviders中移除
cpr.launchingApp = proc;
mLaunchingProviders.add(cpr);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
// 如果是第一次建立的provider,還需要放入mProviderMap
if (firstClass) {
mProviderMap.putProviderByClass(comp, cpr);
}
mProviderMap.putProviderByName(name, cpr);
// 建立privier之間的連結
conn = incProviderCountLocked(r, cpr, token, stable);
if (conn != null) {
conn.waiting = true;
}
}
grantEphemeralAccessLocked(userId, null /*intent*/,
cpi.applicationInfo.uid, UserHandle.getAppId(Binder.getCallingUid()));
}
// 程式碼執行到這裡就說明當前的provider不存在,需要等待對端publish
synchronized (cpr) {
while (cpr.provider == null) {
...
try {
if (conn != null) {
conn.waiting = true;
}
// 在需要獲取的provider上wait
// 直到對端provider被publish之後,方可notify
cpr.wait();
} catch (InterruptedException ex) {
} finally {
if (conn != null) {
conn.waiting = false;
}
}
}
}
return cpr != null ? cpr.newHolder(conn) : null;
}
這段邏輯比較長,總結來說流程如下:
- 校驗caller/許可權等
- 如果provider已經在執行,那麼建立連線
- 更新程序優先順序,如果更新失敗則代表程序被kill了,標記provider不在執行狀態
- provider不在執行狀態,則需要看一下provider是否mProviderMap中曾經有記錄
- 如果沒有需要建立新的ContentProviderRecord物件,並加到mProviderMap
- 判斷程序是否存在
- 如果程序存在則直接排程程序installProvider
- 如果程序不存在則需要先啟動程序
- 新增mProviderMap以及mLaunchingProviders
- 在需要獲取的provider上等待對方notify
4.4.2 incProviderCountLocked
ContentProviderConnection incProviderCountLocked(ProcessRecord r,
final ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
if (r != null) {
for (int i=0; i<r.conProviders.size(); i++) {
ContentProviderConnection conn = r.conProviders.get(i);
if (conn.provider == cpr) {
if (stable) {
conn.stableCount++;
conn.numStableIncs++;
} else {
conn.unstableCount++;
conn.numUnstableIncs++;
}
return conn;
}
}
ContentProviderConnection conn = new ContentProviderConnection(cpr, r);
if (stable) {
conn.stableCount = 1;
conn.numStableIncs = 1;
} else {
conn.unstableCount = 1;
conn.numUnstableIncs = 1;
}
cpr.connections.add(conn);
r.conProviders.add(conn);
startAssociationLocked(r.uid, r.processName, r.curProcState,
cpr.uid, cpr.name, cpr.info.processName);
return conn;
}
cpr.addExternalProcessHandleLocked(externalProcessToken);
return null;
}
這個方法相對簡單,所以沒有寫太多註釋,就是根絕provider是否存在以及是否stable,記錄stable和unstable的數量
4.5 ActivityManagerService.publishContentProviders
上面的流程中多次提到程序啟動的時候會installProvider以及publishContentProviders
,接下來就來看一下到底是怎麼回事,在前面4.1
節中也有提到,在installProvider之後,會通過binder call告訴AMS,publishContentProvider:
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
if (providers == null) {
return;
}
enforceNotIsolatedCaller("publishContentProviders");
synchronized (this) {
final ProcessRecord r = getRecordForAppLocked(caller);
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when publishing content providers");
}
final long origId = Binder.clearCallingIdentity();
final int N = providers.size();
// 遍歷所有已經安裝的provider
for (int i = 0; i < N; i++) {
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
continue;
}
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (dst != null) {
// 將這個providerRecord放入到mProviderMap中
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
mProviderMap.putProviderByClass(comp, dst);
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.putProviderByName(names[j], dst);
}
int launchingCount = mLaunchingProviders.size();
int j;
// 從mLaunchingProviders中移除
boolean wasInLaunchingProviders = false;
for (j = 0; j < launchingCount; j++) {
if (mLaunchingProviders.get(j) == dst) {
mLaunchingProviders.remove(j);
wasInLaunchingProviders = true;
j--;
launchingCount--;
}
}
if (wasInLaunchingProviders) {
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
// notify當前正在等待這個provider唄publish的所有的binder執行緒
synchronized (dst) {
dst.provider = src.provider;
dst.proc = r;
dst.notifyAll();
}
// 更新程序優先順序
updateOomAdjLocked(r, true);
maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
src.info.authority);
}
}
Binder.restoreCallingIdentity(origId);
}
}
- 把所有的已經install的Provider放入到mProviderMap,簡而言之相當於是註冊,讓AMS知道這個provider已經在運行了
- 把provider從從mLaunchingProviders中移除
- 通知等待在這個provider上的binder執行緒,那些binder執行緒就可以拿到provider資訊返回各自程序繼續幹活了
4.6 removeDyingProvider
前面多次說了stable與unstable,而且前面基本都是在操作計數,那麼到底什麼時候會用到這個計數呢?
重點就在這個removeDyingProviderLocked
,這個方法作用是當一個程序死亡之後,把其中的所有的provider也remove掉:
private final boolean removeDyingProviderLocked(ProcessRecord proc,
ContentProviderRecord cpr, boolean always) {
final boolean inLaunching = mLaunchingProviders.contains(cpr);
// 如果這個provider還在等待launching就被remove了
// 需要把當前等待這個provider的執行緒都notifyAll,否則就再也沒有機會notify了
if (!inLaunching || always) {
synchronized (cpr) {
cpr.launchingApp = null;
cpr.notifyAll();
}
// 從mProviderMap中移除
mProviderMap.removeProviderByClass(cpr.name, UserHandle.getUserId(cpr.uid));
String names[] = cpr.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.removeProviderByName(names[j], UserHandle.getUserId(cpr.uid));
}
}
// 判斷這個provider上的所有連結
for (int i = cpr.connections.size() - 1; i >= 0; i--) {
ContentProviderConnection conn = cpr.connections.get(i);
if (conn.waiting) {
if (inLaunching && !always) {
continue;
}
}
ProcessRecord capp = conn.client;
conn.dead = true;
// 如果stableCount大於0,也就是說存在stable的連結
// server掛掉了,那麼就會把client也給kill掉
// 這正是我們前面提到的stable和unstable的重大區別
if (conn.stableCount > 0) {
if (!capp.persistent && capp.thread != null
&& capp.pid != 0
&& capp.pid != MY_PID) {
capp.kill("depends on provider "
+ cpr.name.flattenToShortString()
+ " in dying proc " + (proc != null ? proc.processName : "??")
+ " (adj " + (proc != null ? proc.setAdj : "??") + ")", true);
}
} else if (capp.thread != null && conn.provider.provider != null) {
try {
capp.thread.unstableProviderDied(conn.provider.provider.asBinder());
} catch (RemoteException e) {
}
cpr.connections.remove(i);
if (conn.client.conProviders.remove(conn)) {
stopAssociationLocked(capp.uid, capp.processName, cpr.uid, cpr.name);
}
}
}
if (inLaunching && always) {
mLaunchingProviders.remove(cpr);
}
return inLaunching;
}
5. 總結
經過上面的解讀,主要需要了解到的有以下幾點:
- application初始化的時候會installProvider
- 向AMS請求provider的時候如果對端程序不存在則請求的那個執行緒需要一直等待
- 當對方的程序啟動之後並publish之後,請求provider的執行緒才可返回,所以儘量不要在主執行緒請求provider
- 請求provider分為stable以及unsbale,stable型別連結在server程序掛掉之後,client程序會跟著被株連kill
insert/delete/update
預設建立stable連結,query預設建立unstable連結,如果失敗會重新建立stable連結- AMS作為一箇中間管理員的身份,所有的provider會向它註冊
- 向AMS請求到provider之後,就可以在client和server之間自行binder通訊,不需要再經過systemserver