使用contentprovider發(fā)現(xiàn)的問題
- 在我們的android設(shè)備上有兩個(gè)app,app1提供了一個(gè)contentprovider出去,也就是contentprovider的server端,app2使用了app1提供的contentprovider,也就是contentprovider的client端,當(dāng)app1的進(jìn)程被殺死的時(shí)候會(huì)發(fā)現(xiàn)app2的進(jìn)程也被殺死了,一開始遇到這個(gè)問題的是感覺非常“不講道理”,于是乎就開始查看源碼來“講講道理”
簡(jiǎn)單介紹contentprovider的用法
- contentprovider的使用分為server和client,server端其實(shí)就是數(shù)據(jù)提供端,client端就是數(shù)據(jù)獲取端,在server端的實(shí)現(xiàn)就是定一個(gè)類繼承ContentProvider,然后重寫ContentProvider中定義的query,delete,insert等方法,記得在AndroidManifest.xml文件中配置該自定義的contentprovider;對(duì)于client端就是通過context.getContentResolver()來獲取到一個(gè)ContentResolver對(duì)象,然后調(diào)用對(duì)象的query,delete,update等方法,而當(dāng)調(diào)用這些方法的時(shí)候是如何匹配到相應(yīng)的contentprovider的呢?就是通過方法中的uri參數(shù)來匹配的,詳情繼續(xù)看源碼分析
Contentprovider的調(diào)用流程
- 隨便找一個(gè)ContentResolver的方法,比如query方法:
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder,
@Nullable CancellationSignal cancellationSignal) {
Preconditions.checkNotNull(uri, "uri");
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 {
qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
// reference though, so we might recover!!! Let's try!!!!
// This is exciting!!1!!1!!!!1
unstableProviderDied(unstableProvider);
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
return null;
}
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);
// Wrap the cursor object into CursorWrapperInner object.
final IContentProvider provider = (stableProvider != null) ? stableProvider
: acquireProvider(uri);
final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
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);
}
}
}
Contentprovider也是四大組件之一,支持跨進(jìn)程調(diào)用,因此肯定會(huì)用到IPC的Binder機(jī)制來實(shí)現(xiàn)跨進(jìn)程調(diào)用,在應(yīng)用層就是AIDL
public final IContentProvider acquireUnstableProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
String auth = uri.getAuthority();
if (auth != null) {
return acquireUnstableProvider(mContext, uri.getAuthority());
}
return null;
}
acquireUnstableProvider就是通過URI去獲取到一個(gè)AIDL的定義接口IContentProvider,繼續(xù)往下跟蹤就到了ContextImpl的一個(gè)類ApplicationContentResolver,這個(gè)類繼承自ContentResolver并實(shí)現(xiàn)了里面的acquireUnstableProvider方法,所以acquireUnstableProvider就會(huì)執(zhí)行到ApplicationContentResolver中的acquireUnstableProvider方法:
ContextImpl.java
mContentResolver = new ApplicationContentResolver(this, mainThread, user);
public ContentResolver getContentResolver() {
return mContentResolver;
}
ApplicationContentResolver類
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
以上就已經(jīng)調(diào)用到了ActivityThread的acquireProvider方法了,我們都知道ActivityThread是跟app都在一個(gè)進(jìn)程中的,app進(jìn)程啟動(dòng)的時(shí)候就會(huì)創(chuàng)建ActivityThread,里面定義了Android四大組件和system_process的一些通信接口,其實(shí)就是擔(dān)當(dāng)了四大組件和system_process之間的橋梁的一個(gè)包裝者,而橋梁就是AIDL:
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;
}
// There is a possible race here. Another thread may try to acquire
// the same provider at the same time. When this happens, we want to ensure
// that the first one wins.
// Note that we cannot hold the lock while acquiring and installing the
// provider since it might take a long time to run and it could also potentially
// be re-entrant in the case where the provider is in the same process.
IActivityManager.ContentProviderHolder holder = null;
try {
holder = ActivityManagerNative.getDefault().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;
}
// Install provider will increment the reference count for us, and break
// any ties in the race.
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
上面就已經(jīng)是調(diào)用了ActivityManagerNative的getContentProvider方法了,ActivityManagerNative是ActivityManagerService在app進(jìn)程的一個(gè)AIDL代理,這里已經(jīng)是跨進(jìn)程調(diào)用了,當(dāng)然在進(jìn)行跨進(jìn)程調(diào)用之前會(huì)先檢查是否已經(jīng)有匹配的Contentprovider緩存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()) {
// The hosting process of the provider has died; we can't
// use this one.
Log.i(TAG, "Acquiring provider " + auth + " for user " + userId
+ ": existing object's process dead");
handleUnstableProviderDiedLocked(jBinder, true);
return null;
}
// Only increment the ref count if we have one. If we don't then the
// provider is not reference counted and never needs to be released.
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
incProviderRefLocked(prc, stable);
}
return provider;
}
}
這里先說明一點(diǎn)通過URI去匹配對(duì)應(yīng)的Contentprovider用的是URI中的getAuthority()方法返回的值,這個(gè)值返回什么?看注解:
/**
* Gets the decoded authority part of this URI. For
* server addresses, the authority is structured as follows:
* {@code [ userinfo '@' ] host [ ':' port ]}
*
* <p>Examples: "google.com", "bob@google.com:80"
*
* @return the authority for this URI or null if not present
*/
public abstract String getAuthority();
通俗的將就是返回“主機(jī)域名”,對(duì)于http url為http://domain:port/的就返回domain,而這個(gè)屬性就會(huì)根據(jù)配置文件中配置的Contentprovider的android:authorities屬性去查找到對(duì)應(yīng)的Contentprovider,回到正題,如果acquireExistingProvider返回null,那么這時(shí)候就會(huì)通過aidl取調(diào)用ActivityManagerNative的getContentProvider方法,返回的是IActivityManager.ContentProviderHolder,接下來就跟蹤到ActivityManagerService中的代碼了,getContentProvider方法最終會(huì)調(diào)用到getContentProviderImpl方法中去,代碼太長(zhǎng),只截取getContentProviderImpl方法中的核心部分:
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
checkTime(startTime, "getContentProviderImpl: before getProviderByClass");
cpr = mProviderMap.getProviderByClass(comp, userId);
checkTime(startTime, "getContentProviderImpl: after getProviderByClass");
final boolean firstClass = cpr == null;
if (firstClass) {
final long ident = Binder.clearCallingIdentity();
// If permissions need a review before any of the app components can run,
// we return no provider and launch a review activity if the calling app
// is in the foreground.
if (Build.PERMISSIONS_REVIEW_REQUIRED) {
if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) {
return null;
}
}
try {
checkTime(startTime, "getContentProviderImpl: before getApplicationInfo");
ApplicationInfo ai =
AppGlobals.getPackageManager().
getApplicationInfo(
cpi.applicationInfo.packageName,
STOCK_PM_FLAGS, userId);
checkTime(startTime, "getContentProviderImpl: after getApplicationInfo");
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);
}
}
checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord");
if (r != null && cpr.canRunHere(r)) {
// If this is a multiprocess provider, then just return its
// info and allow the caller to instantiate it. Only do
// this if the provider is the same user as the caller's
// process, or can run as root (so can be in any process).
return cpr.newHolder(null);
}
if (DEBUG_PROVIDER) Slog.w(TAG_PROVIDER, "LAUNCHING REMOTE PROVIDER (myuid "
+ (r != null ? r.uid : null) + " pruid " + cpr.appInfo.uid + "): "
+ cpr.info.name + " callers=" + Debug.getCallers(6));
// This is single process, and our app is now connecting to it.
// See if we are already in the process of launching this
// provider.
final int N = mLaunchingProviders.size();
int i;
for (i = 0; i < N; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
// If the provider is not already being launched, then get it
// started.
if (i >= N) {
final long origId = Binder.clearCallingIdentity();
try {
// Content provider is now in use, its package can't be stopped.
try {
checkTime(startTime, "getContentProviderImpl: before set stopped state");
AppGlobals.getPackageManager().setPackageStoppedState(
cpr.appInfo.packageName, false, userId);
checkTime(startTime, "getContentProviderImpl: after set stopped state");
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ cpr.appInfo.packageName + ": " + e);
}
// Use existing process if already started
checkTime(startTime, "getContentProviderImpl: looking for process record");
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null && !proc.killed) {
if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,
"Installing in existing process " + proc);
if (!proc.pubProviders.containsKey(cpi.name)) {
checkTime(startTime, "getContentProviderImpl: scheduling install");
proc.pubProviders.put(cpi.name, cpr);
try {
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
} else {
checkTime(startTime, "getContentProviderImpl: before start process");
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
checkTime(startTime, "getContentProviderImpl: after start process");
if (proc == null) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
+ name + ": process is bad");
return null;
}
}
cpr.launchingApp = proc;
mLaunchingProviders.add(cpr);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
checkTime(startTime, "getContentProviderImpl: updating data structures");
// Make sure the provider is published (the same provider class
// may be published under multiple names).
if (firstClass) {
mProviderMap.putProviderByClass(comp, cpr);
}
mProviderMap.putProviderByName(name, cpr);
conn = incProviderCountLocked(r, cpr, token, stable);
if (conn != null) {
conn.waiting = true;
}
}
checkTime(startTime, "getContentProviderImpl: done!");
這里就會(huì)根據(jù)之前PackageManagerService從配置文件讀取的Contentprovider信息生成的ProviderInfo對(duì)象中的信息去創(chuàng)建一個(gè)ContentProviderRecord對(duì)象,接下來的會(huì)判斷當(dāng)前申請(qǐng)調(diào)用Contentprovider的client端和Contentprovider的server端是否是同一個(gè)userId或者client端是否是Contentprovider的serv端進(jìn)程的另一個(gè)子進(jìn)程,如果是就直接返回,如果不是就會(huì)判斷Contentprovider的server端進(jìn)程是否啟動(dòng),如果沒有啟動(dòng)就先啟動(dòng)進(jìn)程,然后把創(chuàng)建的ContentProviderRecord對(duì)象緩存起來,到這來Contentprovider的創(chuàng)建過程就結(jié)束了,但是在最后看到一段比較奇怪的代碼:
// Wait for the provider to be published...
synchronized (cpr) {
while (cpr.provider == null) {
if (cpr.launchingApp == null) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
+ name + ": launching app became null");
EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS,
UserHandle.getUserId(cpi.applicationInfo.uid),
cpi.applicationInfo.packageName,
cpi.applicationInfo.uid, name);
return null;
}
try {
if (DEBUG_MU) Slog.v(TAG_MU,
"Waiting to start provider " + cpr
+ " launchingApp=" + cpr.launchingApp);
if (conn != null) {
conn.waiting = true;
}
cpr.wait();
} catch (InterruptedException ex) {
} finally {
if (conn != null) {
conn.waiting = false;
}
}
}
}
看注釋是說等待Contentprovider發(fā)布,那我們就來看看Contentprovider是怎么發(fā)布的,可以得知cpr.provider == null就是未發(fā)布,那么就來網(wǎng)上找之前的代碼發(fā)現(xiàn)當(dāng)創(chuàng)建完ContentProviderRecord后我漏掉了一個(gè)重要的細(xì)節(jié):
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null && !proc.killed) {
if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,
"Installing in existing process " + proc);
if (!proc.pubProviders.containsKey(cpi.name)) {
checkTime(startTime, "getContentProviderImpl: scheduling install");
proc.pubProviders.put(cpi.name, cpr);
try {
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
}
這里就是發(fā)現(xiàn)Contentprovider的server端進(jìn)程如果已經(jīng)啟動(dòng),那么就會(huì)去判斷Contentprovider的發(fā)布緩存中是否有匹配的信息,如果沒有就執(zhí)行scheduleInstallProvider,繼續(xù)跟蹤就回到了ActivityThread類中:
public void scheduleInstallProvider(ProviderInfo provider) {
sendMessage(H.INSTALL_PROVIDER, provider);
}
跟蹤H.INSTALL_PROVIDER就會(huì)發(fā)現(xiàn)在這個(gè)Handler中執(zhí)行了:
public void handleInstallProvider(ProviderInfo info) {
final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
try {
installContentProviders(mInitialApplication, Lists.newArrayList(info));
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
}
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<IActivityManager.ContentProviderHolder> results =
new ArrayList<IActivityManager.ContentProviderHolder>();
for (ProviderInfo cpi : providers) {
if (DEBUG_PROVIDER) {
StringBuilder buf = new StringBuilder(128);
buf.append("Pub ");
buf.append(cpi.authority);
buf.append(": ");
buf.append(cpi.name);
Log.i(TAG, buf.toString());
}
IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
在這里我們看到了Contentprovider的發(fā)布方法publishContentProviders,又回到了ActivityManagerService,繼續(xù)看publishContentProviders:
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
if (providers == null) {
return;
}
enforceNotIsolatedCaller("publishContentProviders");
synchronized (this) {
final ProcessRecord r = getRecordForAppLocked(caller);
if (DEBUG_MU) Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);
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();
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 (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
if (dst != null) {
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;
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);
}
synchronized (dst) {
dst.provider = src.provider;
dst.proc = r;
dst.notifyAll();
}
updateOomAdjLocked(r);
maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
src.info.authority);
}
}
Binder.restoreCallingIdentity(origId);
}
}
這個(gè)方法就比較簡(jiǎn)單了,可以看到在這里對(duì)ContentProviderRecord對(duì)象的provider屬性進(jìn)行了賦值,并且notifyAll:
synchronized (dst) {
dst.provider = src.provider;
dst.proc = r;
dst.notifyAll();
}
所以發(fā)布結(jié)束了,現(xiàn)在我們就來聊聊發(fā)布Contentprovider這個(gè)到底有啥用,可以得知發(fā)布Contentprovider其實(shí)就是對(duì)ContentProviderRecord的provider進(jìn)行初始化,而provider是IContentProvider類型的,這時(shí)候就明白了,IContentProvider是一個(gè)AIDL接口對(duì)應(yīng)的java類,里面提供了AIDL對(duì)應(yīng)的方法,而實(shí)現(xiàn)這個(gè)接口的類是在ContentProvider中的:
try {
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();
if (provider == null) {
Slog.e(TAG, "Failed to instantiate class " +
info.name + " from sourceDir " +
info.applicationInfo.sourceDir);
return null;
}
if (DEBUG_PROVIDER) Slog.v(
TAG, "Instantiating local provider " + info.name);
// XXX Need to create the correct context for this provider.
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;
}
這里是在Contentprovider的server端的進(jìn)程中直接通過反射new了一個(gè)Contentprovider對(duì)象出來,然后通過吧getIContentProvider方法返回的值賦值給provider:
private Transport mTransport = new Transport();
public IContentProvider getIContentProvider() {
return mTransport;
}
而Transport集成ContentProviderNative,ContentProviderNative實(shí)現(xiàn)了IContentProvider,所以ContentProviderNative就是IContentProvider這個(gè)遠(yuǎn)程的AIDL接口提供的本地實(shí)現(xiàn),Contentprovider的跨進(jìn)程通信都是在ContentProviderNative實(shí)現(xiàn)的,到這里provider的初始化就結(jié)束了,現(xiàn)在來理一理provider的作用:Contentprovider的server端是對(duì)外暴露一些接口給Contentprovider的client端使用,這些接口都是通過aidl來實(shí)現(xiàn)通信的,那么如果是server端的進(jìn)程自己調(diào)用自己的Contentprovider的話就直接有l(wèi)ocalProvider,這個(gè)就是provider,因?yàn)榇藭r(shí)是同一個(gè)進(jìn)程所以無需進(jìn)行跨進(jìn)程調(diào)用,如果不是同一個(gè)進(jìn)程,那么這時(shí)候會(huì)server端初始化的provider在之前Contentprovider初始化的時(shí)候已經(jīng)緩存在system_process進(jìn)程的ActivityManagerService的相關(guān)的Contentprovider緩存中了,也就是ContentProviderRecord,然后通過ActivityManagerService的getContentProvider方法把ContentProviderHolder返回給Contentprovider的client端,在ContentProviderHolder中就有provider對(duì)象了,也就是說Contentprovider的server初始化了provider,然后把這個(gè)對(duì)象跨進(jìn)程傳到各個(gè)Contentprovider的client端進(jìn)程給他們使用來進(jìn)行跨進(jìn)程通信,好了,貌似已經(jīng)over了,繼續(xù)回到正題。。。
真正的正題。。。
之前不是說Contentprovider的server端進(jìn)程死了,client端的進(jìn)程也會(huì)被殺死的這個(gè)問題嗎?從log日志發(fā)現(xiàn)打印了這么一段log:
depends on provider...in dying proc...
那么簡(jiǎn)單,找到打印這個(gè)log的地方ActivityManagerService的removeDyingProviderLocked:
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);
}
}
而我發(fā)現(xiàn)我代碼中殺死Contentprovider的server進(jìn)程使用的是ActivityManager的forceStopPackage(通過反射調(diào)用的),殺死進(jìn)程的方法流程就不分析了,反正最終也是走到了ActivityManagerService的cleanUpApplicationRecordLocked方法,再調(diào)用removeDyingProviderLocked方法,然后就會(huì)發(fā)現(xiàn)server端進(jìn)程被kill掉的時(shí)候跟Contentprovider相關(guān)的信息stableCount > 0,于是就殺死了對(duì)應(yīng)的client端進(jìn)程,那么為什么stableCount會(huì)大于0?繼續(xù)分析stableCount是怎么初始化,怎么進(jìn)行賦值變化的,看下ContentResolver的update方法代碼,因?yàn)轫?xiàng)目中確實(shí)調(diào)用了該方法:
public final int update(@RequiresPermission.Write @NonNull Uri uri,
@Nullable ContentValues values, @Nullable String where,
@Nullable String[] selectionArgs) {
Preconditions.checkNotNull(uri, "uri");
IContentProvider provider = acquireProvider(uri);
if (provider == null) {
throw new IllegalArgumentException("Unknown URI " + uri);
}
try {
long startTime = SystemClock.uptimeMillis();
int rowsUpdated = provider.update(mPackageName, uri, values, where, selectionArgs);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, uri, "update", where);
return rowsUpdated;
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return -1;
} finally {
releaseProvider(provider);
}
}
可以看到有acquireProvider和releaseProvider,releaseProvider比較簡(jiǎn)單,先看下releaseProvider方法都做了什么,最終會(huì)調(diào)用到ActivityThread中的releaseProvider方法,只截取有用部分,對(duì)于update方法來說stabled傳的是true:
if (stable) {
if (prc.stableCount == 0) {
if (DEBUG_PROVIDER) Slog.v(TAG,
"releaseProvider: stable ref count already 0, how?");
return false;
}
prc.stableCount -= 1;
if (prc.stableCount == 0) {
// What we do at this point depends on whether there are
// any unstable refs left: if there are, we just tell the
// activity manager to decrement its stable count; if there
// aren't, we need to enqueue this provider to be removed,
// and convert to holding a single unstable ref while
// doing so.
lastRef = prc.unstableCount == 0;
try {
if (DEBUG_PROVIDER) {
Slog.v(TAG, "releaseProvider: No longer stable w/lastRef="
+ lastRef + " - " + prc.holder.info.name);
}
ActivityManagerNative.getDefault().refContentProvider(
prc.holder.connection, -1, lastRef ? 1 : 0);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
}
這里面先對(duì)stableCount減1,然后就執(zhí)行了ActivityManagerService的refContentProvider方法,其中refContentProvider的stable參數(shù)傳的是-1:
public boolean refContentProvider(IBinder connection, int stable, int unstable) {
ContentProviderConnection conn;
try {
conn = (ContentProviderConnection)connection;
} catch (ClassCastException e) {
String msg ="refContentProvider: " + connection
+ " not a ContentProviderConnection";
Slog.w(TAG, msg);
throw new IllegalArgumentException(msg);
}
if (conn == null) {
throw new NullPointerException("connection is null");
}
synchronized (this) {
if (stable > 0) {
conn.numStableIncs += stable;
}
stable = conn.stableCount + stable;
if (stable < 0) {
throw new IllegalStateException("stableCount < 0: " + stable);
}
if (unstable > 0) {
conn.numUnstableIncs += unstable;
}
unstable = conn.unstableCount + unstable;
if (unstable < 0) {
throw new IllegalStateException("unstableCount < 0: " + unstable);
}
if ((stable+unstable) <= 0) {
throw new IllegalStateException("ref counts can't go to zero here: stable="
+ stable + " unstable=" + unstable);
}
conn.stableCount = stable;
conn.unstableCount = unstable;
return !conn.dead;
}
}
這里面的核心操作就是把ContentProviderConnection中的stableCount 進(jìn)行減1操作,好了,既然releaseProvider是對(duì)stableCount 減1,那么acquireProvider很可能是對(duì)stableCount 進(jìn)行加1操作,前面我們已經(jīng)分析過了acquireProvider中首先會(huì)去緩存取所需要的provider信息,如果沒有就會(huì)通過ActivityManagerService的getContentProvider方法去獲取一個(gè)ContentProviderHolder對(duì)象,獲取到了之后會(huì)執(zhí)行installProvider方法,上面在分析provider發(fā)布的時(shí)候已經(jīng)分析過installProvider方法了,那時(shí)候跑的邏輯是通過反射new一個(gè)Contentprovider出來作為localProvider,但是在acquireProvider中去執(zhí)行的installProvider方法此時(shí)跑的就不是這個(gè)邏輯了,因?yàn)榇藭r(shí)provider已經(jīng)成功發(fā)布,所以:
if (holder == null || holder.provider == null) {
...
}
以上條件不會(huì)滿足,而是跑到else分支,并且由于沒有跑if分支所以localProvider沒有初始化,所以:
if (localProvider != null) {
ComponentName cname = new ComponentName(info.packageName, info.name);
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
if (pr != null) {
if (DEBUG_PROVIDER) {
Slog.v(TAG, "installProvider: lost the race, "
+ "using existing local provider");
}
provider = pr.mProvider;
} else {
holder = new IActivityManager.ContentProviderHolder(info);
holder.provider = provider;
holder.noReleaseNeeded = true;
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
}
retHolder = pr.mHolder;
} else {
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
if (DEBUG_PROVIDER) {
Slog.v(TAG, "installProvider: lost the race, updating ref count");
}
// We need to transfer our new reference to the existing
// ref count, releasing the old one... but only if
// release is needed (that is, it is not running in the
// system process).
if (!noReleaseNeeded) {
incProviderRefLocked(prc, stable);
try {
ActivityManagerNative.getDefault().removeContentProvider(
holder.connection, stable);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
} else {
ProviderClientRecord client = installProviderAuthoritiesLocked(
provider, localProvider, holder);
if (noReleaseNeeded) {
prc = new ProviderRefCount(holder, client, 1000, 1000);
} else {
prc = stable
? new ProviderRefCount(holder, client, 1, 0)
: new ProviderRefCount(holder, client, 0, 1);
}
mProviderRefCountMap.put(jBinder, prc);
}
retHolder = prc.holder;
}
以上也是跑else分支,而mProviderRefCountMap.get(jBinder)此時(shí)返回的也肯定是null,因?yàn)槲覀兎治龅腃ontentResolver的update是首次調(diào)用的,所以緩存信息肯定是空的,所以就會(huì)執(zhí)行以下代碼來增加緩存信息:
ProviderClientRecord client = installProviderAuthoritiesLocked(
provider, localProvider, holder);
if (noReleaseNeeded) {
prc = new ProviderRefCount(holder, client, 1000, 1000);
} else {
prc = stable
? new ProviderRefCount(holder, client, 1, 0)
: new ProviderRefCount(holder, client, 0, 1);
}
mProviderRefCountMap.put(jBinder, prc);
ProviderRefCount(IActivityManager.ContentProviderHolder inHolder,
ProviderClientRecord inClient, int sCount, int uCount) {
holder = inHolder;
client = inClient;
stableCount = sCount;
unstableCount = uCount;
}
到這里可以知道當(dāng)noReleaseNeeded為true的時(shí)候創(chuàng)建ProviderRefCount的時(shí)候stableCount傳的是1000,而noReleaseNeeded為false的時(shí)候stableCount才是傳1,那noReleaseNeeded在什么情況下才為true,讓我們來看下ContentProviderHolder是怎么創(chuàng)建出來的,代碼在ActivityManagerService的getContentProviderImpl方法中,ContentProviderHolder是從ContentProviderRecord的newHolder方法創(chuàng)建出來的,然后把ContentProviderRecordnoReleaseNeeded賦值給ContentProviderHolder對(duì)象:
public ContentProviderHolder newHolder(ContentProviderConnection conn) {
ContentProviderHolder holder = new ContentProviderHolder(info);
holder.provider = provider;
holder.noReleaseNeeded = noReleaseNeeded;
holder.connection = conn;
return holder;
}
看ContentProviderRecord是如何創(chuàng)建的:
public ContentProviderRecord(ActivityManagerService _service, ProviderInfo _info,
ApplicationInfo ai, ComponentName _name, boolean _singleton) {
service = _service;
info = _info;
uid = ai.uid;
appInfo = ai;
name = _name;
singleton = _singleton;
noReleaseNeeded = uid == 0 || uid == Process.SYSTEM_UID;
}
這里看到了noReleaseNeeded的初始化條件,uid == 0表示當(dāng)前進(jìn)程是擁有root權(quán)限,uid == Process.SYSTEM_UID是表示當(dāng)前進(jìn)程是系統(tǒng)進(jìn)程,所以通常系統(tǒng)APP進(jìn)程才會(huì)滿足上述條件,因此第三方APP是使用Contentprovider的update方法的時(shí)候noReleaseNeeded通常為false,此時(shí)stableCount == 1,而系統(tǒng)APP在使用Contentprovider的update方法的時(shí)候noReleaseNeeded就為true了,此時(shí)stableCount == 1000,而我遇到這個(gè)問題的場(chǎng)景正好是系統(tǒng)APP,并且APP進(jìn)程擁有root權(quán)限,所以理所當(dāng)然stableCount == 1000,而且我用debug去跟蹤也發(fā)現(xiàn)確實(shí)在創(chuàng)建ProviderRefCount的時(shí)候stableCount參數(shù)的初始化值是1000,releaseProvider方法執(zhí)行后變?yōu)榱?99,那么這里就找到Contentprovider的client端進(jìn)程被殺死的元兇了,就是由于stableCount == 999導(dǎo)致的?這么草率的嗎?這樣下的結(jié)論肯定是不對(duì)的,因?yàn)镻roviderRefCount的初始化是發(fā)生在ActivityThread,而ActivityThread是在app進(jìn)程的,那么stableCount == 999也是在app進(jìn)程中的值,而進(jìn)程被殺死是發(fā)生在ActivityManagerService類中的,ActivityManagerService是系統(tǒng)Service,屬于system_process進(jìn)程,那system_process進(jìn)程種關(guān)于stableCount的值是多少呢?讓我們回到ActivityManagerService的getContentProviderImpl方法,然后再跟蹤到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 (DEBUG_PROVIDER) Slog.v(TAG_PROVIDER,
"Adding provider requested by "
+ r.processName + " from process "
+ cpr.info.processName + ": " + cpr.name.flattenToShortString()
+ " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount);
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;
}
好了,真相大白,ContentProviderConnection創(chuàng)建后stable為true的話stableCount = 1,也就是說在system_process進(jìn)程的stableCount還是1的,那就更尷尬了,既然system_process進(jìn)程的stableCount初始化的時(shí)候是1,那釋放掉的時(shí)候不是就變成0了,那就沒有問題了呀?繼續(xù)看,剛才我們分析了再app進(jìn)程的ProviderRefCount對(duì)象的stableCount在app進(jìn)程是系統(tǒng)進(jìn)程或者有root權(quán)限的時(shí)候初始化值為1000,那么再來看下ActivityThread中releaseProvider方法實(shí)現(xiàn):
if (stable) {
if (prc.stableCount == 0) {
if (DEBUG_PROVIDER) Slog.v(TAG,
"releaseProvider: stable ref count already 0, how?");
return false;
}
prc.stableCount -= 1;
if (prc.stableCount == 0) {
// What we do at this point depends on whether there are
// any unstable refs left: if there are, we just tell the
// activity manager to decrement its stable count; if there
// aren't, we need to enqueue this provider to be removed,
// and convert to holding a single unstable ref while
// doing so.
lastRef = prc.unstableCount == 0;
try {
if (DEBUG_PROVIDER) {
Slog.v(TAG, "releaseProvider: No longer stable w/lastRef="
+ lastRef + " - " + prc.holder.info.name);
}
ActivityManagerNative.getDefault().refContentProvider(
prc.holder.connection, -1, lastRef ? 1 : 0);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
}
stable為true的時(shí)候prc.stableCount的值減1,然后判斷當(dāng)prc.stableCount == 0的時(shí)候才會(huì)去觸發(fā)調(diào)用ActivityManagerService的refContentProvider方法,而refContentProvider方法才是對(duì)ActivityManagerService中的ContentProviderConnection對(duì)象中的stableCount減1,所以當(dāng)app進(jìn)程的stableCount初始化值為1000的時(shí)候,調(diào)用releaseProvider方法,那么app進(jìn)程的stableCount值減1后為999,因此不會(huì)觸發(fā)調(diào)用ActivityManagerService的refContentProvider方法,所以此時(shí)ActivityManagerService中的stableCount仍然為1,所以在執(zhí)行完ContentResoler的update方法后stableCount都不為0,因此在Contentprovider的server端進(jìn)程被殺死的時(shí)候會(huì)順帶殺死Contentprovider的client端進(jìn)程。那么問題來了,如何才能避免Contentprovider的server端被殺死的時(shí)候不會(huì)吧Contentprovider的client端的進(jìn)程也殺死呢,那就確保noReleaseNeeded為false,也就是進(jìn)程的uid != 0 && uid != Process.SYSTEM_UID,也就是進(jìn)程如果是普通的第三方app進(jìn)程的話noReleaseNeeded會(huì)為false那么這時(shí)候就不會(huì)有Contentprovider的server端被殺死了連帶Contentprovider的client進(jìn)程也一起被殺死,我自己寫了一個(gè)demo測(cè)試了下,確實(shí)是這樣的,server端和client端的進(jìn)程互相不影響,而對(duì)于具有系統(tǒng)權(quán)限的進(jìn)程,例如系統(tǒng)進(jìn)程而言就會(huì)出現(xiàn)這個(gè)問題
備注
- 我分析的是我自己遇到的場(chǎng)景,我是在具有root權(quán)限的系統(tǒng)進(jìn)程上uid == 1000的app上發(fā)現(xiàn)有這個(gè)問題,但是第三方app從源碼的角度分析沒看出哪里會(huì)導(dǎo)致這個(gè)問題,但并不代表第三方app使用Contentprovider就不會(huì)有這個(gè)問題,有可能還有其他場(chǎng)景會(huì)導(dǎo)致第三方app使用自定義的Contentprovider的時(shí)候也可能導(dǎo)致這個(gè)問題