开篇
本篇以android-11.0.0_r25作为基础解析
在四大组件中,或许咱们平常用到最少的便是ContentProvider了,ContentProvider是用来帮助运用管理其本身和其他运用所存储数据的拜访,并提供与其他运用同享数据的办法,运用ContentProvider能够安全的在运用之间同享和修改数据,比如说拜访图库,通讯录等
在之前的文章中,咱们提到了ContentProvider的发动机遇,不妨顺水推舟,爽性把这一块剖析个明白,本篇文章并不会教咱们怎样运用ContentProvider,只将精力集中在ContentProvider在体系层面的发动与交互上
基础知识
ContentResolver
想要通过ContentProvider拜访运用数据,咱们一般需求借助ContentResolver的API,咱们能够通过Context.getContentResolver办法获取其实例方针
ContentResolver是一个抽象类,它的抽象办法由ContextImpl.ApplicationContentResolver承继完结,咱们实践上获取到的也是这个实例方针
Uri格式
ContentProvider的运用需求先取得提供者的Uri,它的格式如下:
- Scheme:固定为
content:// - Authority:为提供者在
AndroidManifest里设置的android:authorities特点 - 资源相对途径
- 资源ID
其中,资源相对途径和资源ID不是必须的,要看资源存储的数量及办法
举个栗子,外部存储中某张图片的Uri为:content://media/external/images/media/${id},其中media为Authority,/external/images/media为外部存储图片的相对途径,id为这张图片资源在数据库中存储的id
获取ContentProvider
ContentProvider作为同享数据的桥梁,最主要的几个功用无非是增、删、改、查,咱们就以查作为进口来剖析ContentProvider方针是怎样获取的
//ContentResolver.query
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal) {
...
//测验获取unstableProvider
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
...
try {
//调用长途方针query
qCursor = unstableProvider.query(mPackageName, mAttributionTag, uri, projection,
queryArgs, 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
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
return null;
}
//调用长途方针query
qCursor = stableProvider.query(mPackageName, mAttributionTag, uri, projection,
queryArgs, remoteCancellationSignal);
}
if (qCursor == null) {
return null;
}
// Force query execution. Might fail and throw a runtime exception here.
qCursor.getCount();
...
// Wrap the cursor object into CursorWrapperInner object.
//将qCursor和provider包装成CursorWrapperInner方针回来
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);
}
}
}
咱们能够将这个办法大致分红以下几个进程:
- 获取
unstableProvider长途方针 - 调用
unstableProvider方针的query办法,获取qCursor - 假如
query进程中长途方针逝世,测验获取stableProvider并调用query办法获取qCursor - 获取
stableProvider(假如之前没获取的话) - 将
qCursor和stableProvider包装成CursorWrapperInner方针回来 - 开释资源
已然ContentProvider能够在运用之前同享数据,那它必定是支撑跨进程的,没错,用的仍是咱们熟悉的Binder通信,IContentProvider方针即是给调用方进程运用的长途Binder方针,回顾这个办法咱们发现,IContentProvider长途方针是通过acquireUnstableProvider或acquireProvider获取的,咱们接下来看看这两个办法做了什么
这儿有一个关于unstable和stable的概念,关于通过这两种办法获取的ContentProvider分别会有unstableCount和stableCount两种引证计数,假如长途ContentProvider地点进程逝世,且其stableCount > 0的话,则会将其通过stable办法相关的调用方进程一同杀死,具体的流程咱们会在后边剖析
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;
}
public final IContentProvider acquireProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
final String auth = uri.getAuthority();
if (auth != null) {
return acquireProvider(mContext, auth);
}
return null;
}
public final IContentProvider acquireUnstableProvider(String name) {
if (name == null) {
return null;
}
return acquireUnstableProvider(mContext, name);
}
public final IContentProvider acquireProvider(String name) {
if (name == null) {
return null;
}
return acquireProvider(mContext, name);
}
// ContextImpl.ApplicationContentResolver 内完结
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
// ContextImpl.ApplicationContentResolver 内完结
protected IContentProvider acquireProvider(Context context, String auth) {
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
ActivityThread.acquireProvider
Android体系是通过Authority来区别不同的ContentProvider的,通过一些简略的判别处理后,终究调用了ActivityThread.acquireProvider办法去获取ContentProvider,而acquireUnstableProvider和acquireProvider的区别仅仅终究一个布尔值入参不同算了
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
//测验从本地缓存中获取ContentProvider方针
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.
ContentProviderHolder holder = null;
try {
synchronized (getGetProviderLock(auth, userId)) {
//运用AMS获取ContentProvider方针
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
if (holder == null) {
...
return null;
}
// Install provider will increment the reference count for us, and break
// any ties in the race.
//装置ContentProvider
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
这个办法大约做了以下几件事:
- 首要从缓存中测验获取
IContentProvider方针 - 运用
AMS获取ContentProviderHolder方针 - 装置
ContentProvider - 回来
IContentProvider方针
ActivityThread.acquireExistingProvider
咱们首要看通过acquireExistingProvider办法测验从缓存中获取IContentProvider方针
public final IContentProvider acquireExistingProvider(
Context c, String auth, int userId, boolean stable) {
synchronized (mProviderMap) {
//从缓存Map中查找
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.
//整理ContentProvider
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;
}
}
首要通过Authority和userId来从Map中查找是否已存在对应的ProviderClientRecord方针,然后从中取出IContentProvider方针,再查看其中的长途Binder方针是否已被杀死,终究悉数无误,添加ContentProvider的引证计数
AMS.getContentProvider
假如这一步没有获取到,程序会持续从AMS获取ContentProvider
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String callingPackage, String name, int userId,
boolean stable) {
if (caller == null) {
String msg = "null IApplicationThread when getting content provider "
+ name;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
// The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
// with cross-user grant.
final int callingUid = Binder.getCallingUid();
if (callingPackage != null && mAppOpsService.checkPackage(callingUid, callingPackage)
!= AppOpsManager.MODE_ALLOWED) {
throw new SecurityException("Given calling package " + callingPackage
+ " does not match caller's uid " + callingUid);
}
return getContentProviderImpl(caller, name, null, callingUid, callingPackage,
null, stable, userId);
}
通过一些查看后调用getContentProviderImpl办法,这个办法有点长,咱们分段来看
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
boolean providerRunning = false;
synchronized(this) {
//获取调用方地点进程记载
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller);
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when getting content provider " + name);
}
}
boolean checkCrossUser = true;
// First check if this content provider has been published...
//查看需求的ContentProvider是否已被发布
cpr = mProviderMap.getProviderByName(name, userId);
// If that didn't work, check if it exists for user 0 and then
// verify that it's a singleton provider before using it.
//假如没找到,测验从体系用户中查找已发布的ContentProvider
//并确保它是可用的单例组件,条件如下:
//是用户级运用程序且组件设置了单例flag且具有INTERACT_ACROSS_USERS权限 或 App运转在system进程中 或 组件设置了单例flag且是同一个App
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 == null ? callingUid : r.uid,
cpi.applicationInfo.uid)) {
userId = UserHandle.USER_SYSTEM;
checkCrossUser = false;
} else {
cpr = null;
cpi = null;
}
}
}
//判别ContentProvider地点进程是否已逝世
ProcessRecord dyingProc = null;
if (cpr != null && cpr.proc != null) {
providerRunning = !cpr.proc.killed;
// Note if killedByAm is also set, this means the provider process has just been
// killed by AM (in ProcessRecord.kill()), but appDiedLocked() hasn't been called
// yet. So we need to call appDiedLocked() here and let it clean up.
// (See the commit message on I2c4ba1e87c2d47f2013befff10c49b3dc337a9a7 to see
// how to test this case.)
if (cpr.proc.killed && cpr.proc.killedByAm) {
Slog.wtf(TAG, cpr.proc.toString() + " was killed by AM but isn't really dead");
// Now we are going to wait for the death before starting the new process.
dyingProc = cpr.proc;
}
}
...
}
...
}
首要,榜首部分,查看方针ContentProvider是否已被发布并记载在了mProviderMap中,注意这儿的mProviderMap是AMS中的一个成员变量,一系列Map的一个调集,和ActivityThread中的mProviderMap不是一个东西。假如在当时用户中找不到,且当时用户不是体系用户(UserHandle.USER_SYSTEM == 0),则测验从体系用户中查找合法可用的单例ContentProvider,符合以下任一一个条件的ContentProvider即可被视作单例ContentProvider:
- App是用户级运用程序(uid >= 10000)且
ContentProvider组件设置了单例flag(android:singleUser)且App具有INTERACT_ACROSS_USERS权限 - App运转在
system进程中 -
ContentProvider组件设置了单例flag(android:singleUser)且是同一个App
至于为什么跨用户拜访需求单例这个条件,这个和多用户相关,我也不是很清楚,以后假如剖析到了多用户这块再回来弥补。目前国内厂商的运用分身、手机分身功用大部分用的便是多用户技能
接着通过方针ContentProviderRecord是否存在和其地点进程是否还存活判别方针ContentProvider是否在运转中
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
boolean providerRunning = false;
synchronized(this) {
...
//ContentProvider正在运转中
if (providerRunning) {
cpi = cpr.info;
//假如此ContentProvider能够在调用者进程中直接运转(同一个App的同进程 或 同一个App且Provider组件支撑多进程)
//直接回来一个新的ContentProviderHolder让调用者进程自己发动ContentProvider
if (r != null && cpr.canRunHere(r)) {
... //权限查看
// This provider has been published or is in the process
// of being published... but it is also allowed to run
// in the caller's process, so don't make a connection
// and just let the caller instantiate its own instance.
ContentProviderHolder holder = cpr.newHolder(null);
// don't give caller the provider object, it needs
// to make its own.
holder.provider = null;
return holder;
}
// Don't expose providers between normal apps and instant apps
try {
if (AppGlobals.getPackageManager()
.resolveContentProvider(name, 0 /*flags*/, userId) == null) {
return null;
}
} catch (RemoteException e) {
}
... //权限查看
final long origId = Binder.clearCallingIdentity();
// In this case the provider instance already exists, so we can
// return it right away.
//获取衔接并更新引证计数
conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
stable);
if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
if (cpr.proc != null
&& r != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
// If this is a perceptible app accessing the provider,
// make sure to count it as being accessed and thus
// back up on the LRU list. This is good because
// content providers are often expensive to start.
//更新进程优先级
mProcessList.updateLruProcessLocked(cpr.proc, false, null);
}
}
final int verifiedAdj = cpr.proc.verifiedAdj;
//更新进程adj
boolean success = updateOomAdjLocked(cpr.proc, true,
OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
// XXX things have changed so updateOomAdjLocked doesn't actually tell us
// if the process has been successfully adjusted. So to reduce races with
// it, we will check whether the process still exists. Note that this doesn't
// completely get rid of races with LMK killing the process, but should make
// them much smaller.
if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) {
success = false;
}
maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
// NOTE: there is still a race here where a signal could be
// pending on the process even though we managed to update its
// adj level. Not sure what to do about this, but at least
// the race is now smaller.
if (!success) {
// Uh oh... it looks like the provider's process
// has been killed on us. We need to wait for a new
// process to be started, and make sure its death
// doesn't kill our process.
Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
+ " is crashing; detaching " + r);
//ContentProvider地点进程被杀了,更新引证计数
boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
//仍有别的当地对这个ContentProvider有引证,直接回来null(需求等候进程整理干净才能重启)
if (!lastRef) {
// This wasn't the last ref our process had on
// the provider... we will be killed during cleaning up, bail.
return null;
}
// We'll just start a new process to host the content provider
//将运转状况标为false,使得重新发动ContentProvider地点进程
providerRunning = false;
conn = null;
dyingProc = cpr.proc;
} else {
cpr.proc.verifiedAdj = cpr.proc.setAdj;
}
Binder.restoreCallingIdentity(origId);
}
...
}
...
}
第二部分,假如方针ContentProvider正在运转中,首要查看方针ContentProvider是否能够在调用者进程中直接运转,需求满足以下任一一个条件:
- 调用者和方针
ContentProvider是同一个App中的同进程 - 调用者和方针
ContentProvider属同一个App且ContentProvider组件支撑多进程(android:multiprocess)
在这种状况下,直接回来一个新的ContentProviderHolder让调用者进程自己处理取得ContentProvider即可,具体逻辑在ActivityThread.installProvider办法中,后边会剖析
假如不满足这种状况,即调用方进程和方针ContentProvider不在一个进程中,需求跨进程调用,获取ContentProviderConnection衔接并更新引证计数
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
boolean providerRunning = false;
synchronized(this) {
...
//ContentProvider未在运转
if (!providerRunning) {
//通过PMS获取ContentProvider信息
try {
cpi = AppGlobals.getPackageManager().
resolveContentProvider(name,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
} catch (RemoteException ex) {
}
if (cpi == null) {
return null;
}
// If the provider is a singleton AND
// (it's a call within the same user || the provider is a
// privileged app)
// Then allow connecting to the singleton provider
boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r == null ? callingUid : r.uid,
cpi.applicationInfo.uid);
if (singleton) {
userId = UserHandle.USER_SYSTEM;
}
cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
... //各项查看
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
//通过Class(android:name特点)获取ContentProviderRecord
cpr = mProviderMap.getProviderByClass(comp, userId);
//此ContentProvider是榜首次运转
boolean firstClass = cpr == null;
if (firstClass) {
final long ident = Binder.clearCallingIdentity();
... //权限处理
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);
//新建ContentProvider记载
cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
} finally {
Binder.restoreCallingIdentity(ident);
}
} else if (dyingProc == cpr.proc && dyingProc != null) {
// The old stable connection's client should be killed during proc cleaning up,
// so do not re-use the old ContentProviderRecord, otherwise the new clients
// could get killed unexpectedly.
//旧的ContentProvider进程在逝世进程中,不要复用旧的ContentProviderRecord,避免呈现预期之外的问题
cpr = new ContentProviderRecord(cpr);
// This is sort of "firstClass"
firstClass = true;
}
//假如此ContentProvider能够在调用者进程中直接运转(同一个App的同进程 或 同一个App且Provider组件支撑多进程)
//直接回来一个新的ContentProviderHolder让调用者进程自己发动ContentProvider
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);
}
// This is single process, and our app is now connecting to it.
// See if we are already in the process of launching this
// provider.
//查找正在发动中的ContentProvider
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.
//方针ContentProvider不在发动中
if (i >= N) {
final long origId = Binder.clearCallingIdentity();
try {
// Content provider is now in use, its package can't be stopped.
//将App状况置为unstopped,设置休眠状况为false
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);
}
// Use existing process if already started
//获取方针ContentProvider地点进程记载
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null && !proc.killed) { //进程存活
if (!proc.pubProviders.containsKey(cpi.name)) {
//将ContentProviderRecord保存到进程已发布ContentProvider列表中
proc.pubProviders.put(cpi.name, cpr);
try {
//调度ActivityThread直接装置ContentProvider
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
} else { //进程逝世
//发动App(App发动进程中会主动发动ContentProvider)
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0,
new HostingRecord("content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name)),
ZYGOTE_POLICY_FLAG_EMPTY, false, false, false);
if (proc == null) {
...
return null;
}
}
cpr.launchingApp = proc;
//将方针ContentProvider添加到发动中列表
mLaunchingProviders.add(cpr);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
// 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, callingUid, callingPackage, callingTag,
stable);
if (conn != null) {
conn.waiting = true;
}
}
grantImplicitAccess(userId, null /*intent*/, callingUid,
UserHandle.getAppId(cpi.applicationInfo.uid));
}
...
}
第三部分,假如方针ContentProvider未在运转,先通过PMS获取ContentProvider信息,接着测验通过Class(android:name特点)获取ContentProviderRecord,假如获取不到,说明这个ContentProvider是榜首次运转(开机后),这种状况下需求新建ContentProviderRecord,假如获取到了,可是其地点进程被标记为正在逝世,此刻同样需求新建ContentProviderRecord,不要复用旧的ContentProviderRecord,避免呈现预期之外的问题
接下来同样查看方针ContentProvider是否能够在调用者进程中直接运转,假如能够直接回来一个新的ContentProviderHolder让调用者进程自己发动获取ContentProvider
接着查看正在发动中的ContentProvider列表,假如不在列表中,咱们或许需求手动发动它,此刻又有两种状况:
-
ContentProvider地点进程已发动:假如进程已发布ContentProvider列表中不包括这个ContentProviderRecord,则将其添加到列表中,然后调用方针进程中的ApplicationThread.scheduleInstallProvider办法装置发动ContentProvider -
ContentProvider地点进程未发动:发动方针进程,方针进程发动进程中会主动装置发动ContentProvider(ActivityThread.handleBindApplication办法中)
终究更新mProviderMap,获取ContentProviderConnection衔接并更新引证计数
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
boolean providerRunning = false;
// Wait for the provider to be published...
final long timeout =
SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS;
boolean timedOut = false;
synchronized (cpr) {
while (cpr.provider == null) {
//ContentProvider发动进程中进程逝世,回来null
if (cpr.launchingApp == null) {
...
return null;
}
try {
//计算最大等候时刻
final long wait = Math.max(0L, timeout - SystemClock.uptimeMillis());
if (conn != null) {
conn.waiting = true;
}
//开释锁,等候ContentProvider发动完结
cpr.wait(wait);
//等候时刻已过,ContentProvider仍是没能发动完结并发布,超时
if (cpr.provider == null) {
timedOut = true;
break;
}
} catch (InterruptedException ex) {
} finally {
if (conn != null) {
conn.waiting = false;
}
}
}
}
if (timedOut) {
... //超时处理
return null;
}
//回来新的ContentProviderHolder
return cpr.newHolder(conn);
}
第四部分,假如ContentProvider已存在,直接新建一个ContentProviderHolder回来,假如ContentProvider之前不存在,现在正在发动中,则以当时时刻加上CONTENT_PROVIDER_READY_TIMEOUT_MILLIS推算出一个超时时刻,给方针ContentProviderRecord上锁后,调用wait办法等候,直到ContentProvider成功发布后notify免除wait状况(在AMS.publishContentProviders办法中,之后会剖析到),或一直等候直到超时。wait状况免除后,判别内部ContentProvider是否已被赋值,假如没有,则能够判定超时,此刻回来null,如有,则回来一个新的ContentProviderHolder
ActivityThread.installProvider
由于这个办法一同包括了发动装置本地ContentProvider和获取装置长途ContentProvider的逻辑,所以放到后边发动ContentProvider章节里一同剖析
发动ContentProvider
从前面的章节获取ContentProvider中,咱们现已归纳出ContentProvider的发动分为两种状况,接着咱们就来剖析在这两种状况下,ContentProvider的发动途径
进程已发动
在进程已发动的状况下,假如进程已发布ContentProvider列表中不包括这个ContentProviderRecord,则将其添加到列表中,然后调用方针进程中的ApplicationThread.scheduleInstallProvider办法装置发动ContentProvider
ApplicationThread.scheduleInstallProvider会通过Hander发送一条what值为H.INSTALL_PROVIDER的音讯,咱们依据这个what值查找,发现会走到ActivityThread.handleInstallProvider办法中,在这个办法内又会调用installContentProviders办法装置发动ContentProvider
进程未发动
在进程未发动的状况下,直接发动方针进程,在之前的文章 Android源码剖析 – Activity发动流程(中) 里,咱们剖析了App的发动流程,其中有两个当地对发动ContentProvider至关重要
AMS.attachApplicationLocked
在这个办法中会调用generateApplicationProvidersLocked办法
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
...
//normalMode一般状况下均为true
List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
...
}
private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
List<ProviderInfo> providers = null;
try {
//通过PMS获取App中同一个进程内的一切的ContentProvider组件信息
providers = AppGlobals.getPackageManager()
.queryContentProviders(app.processName, app.uid,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS
| MATCH_DEBUG_TRIAGED_MISSING, /*metadastaKey=*/ null)
.getList();
} catch (RemoteException ex) {
}
int userId = app.userId;
if (providers != null) {
int N = providers.size();
//有必要的状况下进行Map扩容
app.pubProviders.ensureCapacity(N + app.pubProviders.size());
for (int i=0; i<N; i++) {
// TODO: keep logic in sync with installEncryptionUnawareProviders
ProviderInfo cpi =
(ProviderInfo)providers.get(i);
//关于单例ContentProvider,需求在默许用户中发动,假如不是默许用户的话则直接将其丢掉掉,不发动
boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags);
if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_SYSTEM) {
// This is a singleton provider, but a user besides the
// default user is asking to initialize a process it runs
// in... well, no, it doesn't actually run in this process,
// it runs in the process of the default user. Get rid of it.
providers.remove(i);
N--;
i--;
continue;
}
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
if (cpr == null) {
//新建ContentProviderRecord并将其加入到缓存中
cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton);
mProviderMap.putProviderByClass(comp, cpr);
}
//将ContentProviderRecord保存到进程已发布ContentProvider列表中
app.pubProviders.put(cpi.name, cpr);
if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
// Don't add this if it is a platform component that is marked
// to run in multiple processes, because this is actually
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
//将App添加至进程中运转的包列表中
app.addPackage(cpi.applicationInfo.packageName,
cpi.applicationInfo.longVersionCode, mProcessStats);
}
notifyPackageUse(cpi.applicationInfo.packageName,
PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER);
}
}
return providers;
}
这个办法主要是获取需求发动的ContentProvider的ContentProviderRecord,假如是榜首次发动这个ContentProvider则需求新建一个ContentProviderRecord并将其存入缓存,然后将其保存到进程已发布ContentProvider列表中,以供后边运用。一同这个办法回来了需求发动的ProviderInfo列表,AMS.attachApplicationLocked办法能够依据这个列表判别是否有需求发动的ContentProvider并设置ContentProvider发动超时检测
ActivityThread.handleBindApplication
private void handleBindApplication(AppBindData data) {
...
try {
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
//创立Application
app = data.info.makeApplication(data.restrictedBackupMode, null);
...
mInitialApplication = app;
// don't bring up providers in restricted mode; they may depend on the
// app's custom Application class
//在非受限形式下发动ContentProvider
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);
}
}
...
//履行Application的onCreate办法
try {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
...
}
} finally {
// If the app targets < O-MR1, or doesn't change the thread policy
// during startup, clobber the policy to maintain behavior of b/36951662
if (data.appInfo.targetSdkVersion < Build.VERSION_CODES.O_MR1
|| StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) {
StrictMode.setThreadPolicy(savedPolicy);
}
}
...
}
能够看到,在这个办法中直接调用了installContentProviders办法装置发动ContentProvider
另外提一点,为什么我要把Application的创立和onCreate也放进来呢?现在市面上有许多库,包括许多教程告知咱们,能够通过注册ContentProvider的办法初始化SDK,获取大局Context,比如说著名的内存走漏检测工具LeakCanary的新版别,想要运用它,直接添加它的依赖就行了,不需求对代码做哪怕一点的改动,究其原理,便是由于ContentProvider的发动机遇是在Application创立后,Application.onCreate调用前,而且ContentProvider内的Context成员变量大约率便是Application,咱们以后在开发进程中也能够妙用这一点
ActivityThread.installContentProviders
好了,现在这两种状况终究都走到了ActivityThread.installContentProviders办法中,那咱们接下来就好好剖析ContentProvider实践的发动装置流程
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
for (ProviderInfo cpi : providers) {
//逐一发动
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
//发布ContentProvider
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
这个办法很简略,便利一切待发动的ContentProvider信息列表,逐一发动装置ContentProvider,终究一同发布
ActivityThread.installProvider
咱们先看installProvider办法,咱们在上一章中剖析到,获取ContentProvider的时分也会调用这个办法,这次咱们就结合起来一同剖析
通过上文的代码,咱们发现,有两处当地会调用installProvider办法,办法的入参有三种办法,分别为:
-
holder不为null,info不为null,holder.provider为null:在ActivityThread.acquireProvider办法中被调用,途径为 没有获取到已存在的ContentProvider->AMS.getContentProvider->AMS.getContentProviderImpl-> 发现方针ContentProvider能够在调用者进程中直接运转 -> 直接回来一个新的ContentProviderHolder(包括ProviderInfo) ->ActivityThread.installProvider,在这种状况下installProvider办法会在本地发动装置ContentProvider -
holder为null,info不为null:在ActivityThread.installContentProviders办法中被调用,两条途径,一是App进程发动后主动履行,二是在AMS.getContentProvider办法中发现方针进程已发动可是ContentProvider未发动,调用ActivityThread.scheduleInstallProvider办法履行,在这种状况下installProvider办法会在本地发动装置ContentProvider -
holder不为null,holder.provider不为null:在ActivityThread.acquireProvider办法中被调用,途径为 没有获取到已存在的ContentProvider->AMS.getContentProvider->AMS.getContentProviderImpl-> 获取到方针进程的长途ContentProvider引证 -> 包装成ContentProviderHolder回来 ->ActivityThread.installProvider,在这种状况下installProvider办法直接能够获取到长途ContentProvider引证,然后进行处理
咱们将这三种状况分红两种case分别剖析
本地发动ContentProvider
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null) { //发动本地ContentProvider
Context c = null;
ApplicationInfo ai = info.applicationInfo;
//首要获取Context,一般状况下便是Application
if (context.getPackageName().equals(ai.packageName)) {
c = context;
} else if (mInitialApplication != null &&
mInitialApplication.getPackageName().equals(ai.packageName)) {
c = mInitialApplication;
} else {
try {
c = context.createPackageContext(ai.packageName,
Context.CONTEXT_INCLUDE_CODE);
} catch (PackageManager.NameNotFoundException e) {
// Ignore
}
}
if (c == null) {
return null;
}
//Split Apks动态加载相关
if (info.splitName != null) {
try {
c = c.createContextForSplit(info.splitName);
} catch (NameNotFoundException e) {
throw new RuntimeException(e);
}
}
try {
final java.lang.ClassLoader cl = c.getClassLoader();
//获取运用信息
LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
if (packageInfo == null) {
// System startup case.
packageInfo = getSystemContext().mPackageInfo;
}
//通过AppComponentFactory实例化ContentProvider
localProvider = packageInfo.getAppFactory()
.instantiateProvider(cl, info.name);
//Transport类,承继自ContentProviderNative(Binder服务端)
provider = localProvider.getIContentProvider();
if (provider == null) {
return null;
}
// XXX Need to create the correct context for this provider.
//初始化ContentProvider,调用其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 { //获取外部ContentProvider
...
}
ContentProviderHolder retHolder;
synchronized (mProviderMap) {
//关于本地ContentProvider来说,这儿的实践类型是Transport,承继自ContentProviderNative(Binder服务端)
IBinder jBinder = provider.asBinder();
if (localProvider != null) { //本地发动ContentProvider的状况
ComponentName cname = new ComponentName(info.packageName, info.name);
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
if (pr != null) {
//假如现已存在相应的ContentProvider记载,运用其内部已存在的ContentProvider
provider = pr.mProvider;
} else {
//不然运用新创立的ContentProvider
holder = new ContentProviderHolder(info);
holder.provider = provider;
//关于本地ContentProvider来说,不存在开释引证这种状况
holder.noReleaseNeeded = true;
//创立ProviderClientRecord并将其保存到mProviderMap本地缓存中
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
//保存ProviderClientRecord到本地缓存中
mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
}
retHolder = pr.mHolder;
} else { //获取长途ContentProvider的状况
...
}
}
return retHolder;
}
咱们在这儿找到了ContentProvider创立并发动的进口,首要通过传入的Context(实践上便是Application)判别并确认创立并给ContentProvider运用的的Context是什么(一般状况下也是Application),然后获取到运用信息LoadedApk,再通过它得到AppComponentFactory(前面的文章中介绍过,假如没有在AndroidManifest中设置android:appComponentFactory特点,运用的便是默许的AppComponentFactory),接着通过AppComponentFactory.instantiateProvider办法实例化ContentProvider方针
public @NonNull ContentProvider instantiateProvider(@NonNull ClassLoader cl,
@NonNull String className)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (ContentProvider) cl.loadClass(className).newInstance();
}
默许的话便是通过ClassName反射调用默许结构函数实例化ContentProvider方针,终究再调用ContentProvider.attachInfo办法初始化
public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
...
if (mContext == null) {
mContext = context;
...
ContentProvider.this.onCreate();
}
}
详细内容咱们就不剖析了,只需求知道这儿给mContext赋了值,然后调用了ContentProvider.onCreate办法就能够了
到了这一步,ContentProvider就算是发动完结了,接下来需求履行一些装置进程,其实也便是对缓存等进行一些处理。在ContentProvider实例化后,会调用其getIContentProvider办法给provider变量赋值,这儿取得的方针其实是一个Transport方针,承继自ContentProviderNative,是一个Binder服务端方针,在ContentProvider初始化后,会对Transport方针调用asBinder办法取得Binder方针,这儿取得的其实仍是自己本身,接着从缓存中测验获取ProviderClientRecord方针,假如获取到了,说明现已存在了相应的ContentProvider,运用ProviderClientRecord内部的ContentProvider,刚刚新创立的那个就能够丢掉了,假如没获取到,就去新建ContentProviderHolder以及ProviderClientRecord,然后将他们添加到各种缓存中,至此,ContentProvider的装置进程也到此完毕
获取处理长途ContentProvider
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null) { //发动本地ContentProvider
...
} else { //获取外部ContentProvider
//实践类型为ContentProviderProxy
provider = holder.provider;
}
ContentProviderHolder retHolder;
synchronized (mProviderMap) {
//关于外部ContentProvider来说,这儿的实践类型是BinderProxy
IBinder jBinder = provider.asBinder();
if (localProvider != null) { //本地发动ContentProvider的状况
...
} else { //获取长途ContentProvider的状况
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) { //假如ContentProvider引证已存在
// 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).
//关于长途ContentProvider来说,假如方针App为system运用(UID为ROOT_UID或SYSTEM_UID)
//而且方针App不为设置(包名不为com.android.settings),则noReleaseNeeded为true
if (!noReleaseNeeded) {
//添加已存在的ContentProvider引证的引证计数
incProviderRefLocked(prc, stable);
try {
//开释传入的引证,移除ContentProviderConnection相关信息,更新引证计数
ActivityManager.getService().removeContentProvider(
holder.connection, stable);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
} else {
//创立ProviderClientRecord并将其保存到mProviderMap本地缓存中
ProviderClientRecord client = installProviderAuthoritiesLocked(
provider, localProvider, holder);
if (noReleaseNeeded) { //同上,方针App为system运用,不需求开释引证
//新建一个ProviderRefCount,但引证计数初始化为一个较大的数值
//这样后续不管调用方进程的ContentProvider引证计数如何变动都不会影响到AMS
prc = new ProviderRefCount(holder, client, 1000, 1000);
} else { //需求开释引证的状况下
//正常的新建初始化一个ProviderRefCount
prc = stable
? new ProviderRefCount(holder, client, 1, 0)
: new ProviderRefCount(holder, client, 0, 1);
}
//保存至缓存
mProviderRefCountMap.put(jBinder, prc);
}
retHolder = prc.holder;
}
}
return retHolder;
}
关于holder.provider不为null的状况,直接获取长途ContentProvider引证,然后进行处理就能够了。这儿获取到的IContentProvider的实践类型是ContentProviderProxy,然后对其调用asBinder办法,获取到的是BinderProxy方针,接着从缓存中测验获取ProviderRefCount方针,假如缓存中现已有相应的引证方针了,则在需求开释引证(!noReleaseNeeded)的状况下运用原有的引证,开释参数传入进来的ContentProvider引证
这儿noReleaseNeeded是在ContentProviderRecord结构时赋值的,为true的条件是方针App为system运用(UID为ROOT_UID或SYSTEM_UID)而且方针App不为设置(包名不为com.android.settings)
假如缓存中没有查找到相应的ProviderRefCount方针,新建ProviderClientRecord和ProviderRefCount方针,并将他们保存到缓存中,至于为什么在noReleaseNeeded的状况下,新建的ProviderRefCount的引证计数初始值为1000,我猜想是由于noReleaseNeeded代表了不需求开释引证,所以这儿爽性设置一个比较大的值,这样不管调用方进程的ContentProvider引证计数怎样变动,都不会再调用到AMS的办法中去处理引证的变化,在非常前期的Android版别中(Android 4.0.1),这个值曾被设置为10000
至此,长途ContentProvider的装置也完毕了
ActivityThread.installProviderAuthoritiesLocked
接下来咱们再简略的看一下两种case都会走到的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);
...
final ProviderClientRecord pcr = new ProviderClientRecord(
auths, provider, localProvider, holder);
for (String auth : auths) {
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;
}
这个办法很简略,新建了一个ProviderClientRecord并将其添加到mProviderMap缓存中,这儿的操作对应着最前面的acquireExistingProvider办法,有了这个缓存,以后就能够直接拿,而不用再复杂的通过一系列的AMS跨进程操作了
AMS.publishContentProviders
ContentProvider悉数发动装置完后,便要调用AMS.publishContentProviders将他们发布出去,供外部运用了
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
if (providers == null) {
return;
}
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();
for (int i = 0; i < N; i++) {
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
continue;
}
//App进程发动时或AMS.getContentProvider中现已将相应ContentProviderRecord添加到了pubProviders中
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
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);
}
//ContentProvider现已发动完毕,将其从正在发动的ContentProvider列表中移除
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--;
}
}
//移除ContentProvider发动超时监听
if (wasInLaunchingProviders) {
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
// Make sure the package is associated with the process.
// XXX We shouldn't need to do this, since we have added the package
// when we generated the providers in generateApplicationProvidersLocked().
// But for some reason in some cases we get here with the package no longer
// added... for now just patch it in to make things happy.
r.addPackage(dst.info.applicationInfo.packageName,
dst.info.applicationInfo.longVersionCode, mProcessStats);
synchronized (dst) {
dst.provider = src.provider;
dst.setProcess(r);
//让出锁,告诉其他wait的当地
//对应着AMS.getContentProvider的第四部分:等候ContentProvider发动完结
dst.notifyAll();
}
dst.mRestartCount = 0;
updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
src.info.authority);
}
}
Binder.restoreCallingIdentity(origId);
}
}
遍历整个待发布的ContentProvider列表,从ProcessRecord.pubProviders中查找相对应的ContentProviderRecord,咱们在之前的章节中现已剖析过了,App进程发动时或AMS.getContentProvider中现已将相应ContentProviderRecord添加到了pubProviders中,然后便是将其保存到各个缓存中,由于ContentProvider现已发动完毕,所以需求将其从正在发动的ContentProvider列表中移除,在ContentProvider正常发动的状况下,咱们需求将ContentProvider的发动超时监听移除,终究,获取ContentProviderRecord同步锁,将准备好的ContentProvider赋值到ContentProviderRecord中,接着调用notifyAll办法告诉其他调用过wait的当地,将锁让出,这儿对应的便是AMS.getContentProvider的第四部分:等候ContentProvider发动完结
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
...
//这儿的cpr和在publishContentProviders取得的dst是一个方针
synchronized (cpr) {
while (cpr.provider == null) {
...
//开释锁,等候ContentProvider发动完结
cpr.wait(wait);
...
}
}
...
}
这样,ContentProvider一发布,这儿就会收到告诉,免除wait状况,取得到ContentProvider,回来出去,是不是感觉悉数都串起来了?
ContentProvider引证计数
ContentProvider的获取与发动剖析完了,接下来咱们聊聊它的引证计数,为下一小节剖析ContentProvider逝世杀死调用方进程的进程做准备
ActivityThread层的引证计数是和AMS层的引证计数分开的,ActivityThread记载的是方针ContentProvider在本进程中有多少处正在运用,而AMS记载的是方针ContentProvider正在被多少个进程运用
ActivityThread层的引证计数
添加引证计数
咱们先从ActivityThread层添加引证计数开端说起,在ActivityThread获取ContentProvider时,便会调用incProviderRefLocked办法来添加引证计数,具体的机遇为acquireExistingProvider或installProvider时,代码我就不重复放了,咱们看前面几个小节就行(后同)
private final void incProviderRefLocked(ProviderRefCount prc, boolean stable) {
if (stable) {
//添加ActivityThread的stable引证计数
prc.stableCount += 1;
//本进程对方针ContentProvider产生了stable引证联系
if (prc.stableCount == 1) {
// We are acquiring a new stable reference on the provider.
int unstableDelta;
//正在移除ContentProvider引证中(开释ContentProvider后发现stable和unstable引证计数均为0)
if (prc.removePending) {
// We have a pending remove operation, which is holding the
// last unstable reference. At this point we are converting
// that unstable reference to our new stable reference.
//当ActivityThread开释一个stable的ContentProvider时,假如开释完后,
//发现stable和unstable引证计数均为0,则会暂时保存一个unstable引证
//所以这儿需求为 -1 ,将这个unstable引证移除
unstableDelta = -1;
// Cancel the removal of the provider.
prc.removePending = false;
// There is a race! It fails to remove the message, which
// will be handled in completeRemoveProvider().
//撤销移除ContentProvider引证
mH.removeMessages(H.REMOVE_PROVIDER, prc);
} else {
//关于正常状况,只需求添加stable引证计数,不需求动unstable引证计数
unstableDelta = 0;
}
try {
//AMS层修改引证计数
ActivityManager.getService().refContentProvider(
prc.holder.connection, 1, unstableDelta);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
} else {
//添加ActivityThread的unstable引证计数
prc.unstableCount += 1;
//本进程对方针ContentProvider产生了unstable引证联系
if (prc.unstableCount == 1) {
// We are acquiring a new unstable reference on the provider.
//正在移除ContentProvider引证中(开释ContentProvider后发现stable和unstable引证计数均为0)
if (prc.removePending) {
// Oh look, we actually have a remove pending for the
// provider, which is still holding the last unstable
// reference. We just need to cancel that to take new
// ownership of the reference.
//撤销移除ContentProvider引证
prc.removePending = false;
mH.removeMessages(H.REMOVE_PROVIDER, prc);
} else {
// First unstable ref, increment our count in the
// activity manager.
try {
//添加AMS层的unstable引证计数
ActivityManager.getService().refContentProvider(
prc.holder.connection, 0, 1);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
}
}
}
这儿的逻辑需求配合着ContentProvider开释引证那里一同看才好理解,我先提前解释一下
首要,removePending这个变量表示此ContentProvider正在移除中,当ActivityThread削减引证计数,查看到stable和unstable引证计数均为0后被赋值为true,而且会向Handler发送一条what值为REMOVE_PROVIDER的延时音讯,在必定时刻后便会触发ContentProvider移除操作,整理本地缓存,再将removePending重新置为false,所以当这儿removePending为true则说明此ContentProvider还没完全被移除,咱们把这个音讯撤销掉持续运用这个ContentProvider
关于stable引证的状况下,当ActivityThread削减引证计数,查看到stable和unstable引证计数均为0后,会暂时保存一个unstable引证,等到后边真实触发到了移除ContentProvider的时分再将这个unstable引证移除,所以在添加引证计数的时分需求考虑到这一点,在这种状况下要将AMS层的unstable引证计数减一
关于其他的状况便是正常的添加ActivityThread层引证计数,然后调用AMS.refContentProvider办法操作AMS层的引证计数
削减引证计数
ContentProvider运用完后会调用ActivityThread.releaseProvider办法,以query办法为例,终究会调用releaseUnstableProvider和releaseProvider办法,终究都会走到这儿来
public final boolean releaseProvider(IContentProvider provider, boolean stable) {
if (provider == null) {
return false;
}
IBinder jBinder = provider.asBinder();
synchronized (mProviderMap) {
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc == null) {
// The provider has no ref count, no release is needed.
return false;
}
boolean lastRef = false;
if (stable) {
//引证计数现已为0,无法再减了
if (prc.stableCount == 0) {
return false;
}
//削减ActivityThread的stable引证计数
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 {
//假如是终究的引证,则暂时保存一个unstable引证
ActivityManager.getService().refContentProvider(
prc.holder.connection, -1, lastRef ? 1 : 0);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
} else {
//引证计数现已为0,无法再减了
if (prc.unstableCount == 0) {
return false;
}
//削减ActivityThread的unstable引证计数
prc.unstableCount -= 1;
if (prc.unstableCount == 0) {
// If this is the last reference, we need to enqueue
// this provider to be removed instead of telling the
// activity manager to remove it at this point.
lastRef = prc.stableCount == 0;
//假如是终究的引证,则不进入到这儿,暂时保存一个unstable引证
if (!lastRef) {
try {
//削减AMS引证计数
ActivityManager.getService().refContentProvider(
prc.holder.connection, 0, -1);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
}
}
if (lastRef) {
if (!prc.removePending) {
// Schedule the actual remove asynchronously, since we don't know the context
// this will be called in.
//表面此ContentProvider正在移除中
prc.removePending = true;
//发送延时音讯,等候必定时刻后移除ContentProvider
Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, prc);
mH.sendMessageDelayed(msg, CONTENT_PROVIDER_RETAIN_TIME);
} else {
Slog.w(TAG, "Duplicate remove pending of provider " + prc.holder.info.name);
}
}
return true;
}
}
能够看到,在减完引证计数后,假如发现是终究一个引证,即stable和unstable引证计数均为0,此刻不管是stable仍是unstable都会让AMS暂时保存一个unstable引证,然后发送一条what值为REMOVE_PROVIDER的延时音讯,等候必定时刻后移除ContentProvider,当时刻到了触发这条音讯时,会调用到ActivityThread.completeRemoveProvider办法
final void completeRemoveProvider(ProviderRefCount prc) {
synchronized (mProviderMap) {
if (!prc.removePending) {
// There was a race! Some other client managed to acquire
// the provider before the removal was completed.
// Abort the removal. We will do it later.
return;
}
// More complicated race!! Some client managed to acquire the
// provider and release it before the removal was completed.
// Continue the removal, and abort the next remove message.
prc.removePending = false;
//移除缓存
final IBinder jBinder = prc.holder.provider.asBinder();
ProviderRefCount existingPrc = mProviderRefCountMap.get(jBinder);
if (existingPrc == prc) {
mProviderRefCountMap.remove(jBinder);
}
//移除缓存
for (int i=mProviderMap.size()-1; i>=0; i--) {
ProviderClientRecord pr = mProviderMap.valueAt(i);
IBinder myBinder = pr.mProvider.asBinder();
if (myBinder == jBinder) {
mProviderMap.removeAt(i);
}
}
}
try {
//处理AMS层引证计数
ActivityManager.getService().removeContentProvider(
prc.holder.connection, false);
} catch (RemoteException e) {
//do nothing content provider object is dead any way
}
}
这个办法将进程内所持有的ContentProvider相关缓存铲除,然后调用AMS.removeContentProvider办法告诉AMS移除ContentProvider,处理相应的引证计数。这儿咱们发现,调用AMS.removeContentProvider办法传入的终究一个参数stable为false,由于咱们之前在stable和unstable引证计数均为0的状况下,保存了一个unstable引证,所以这时移除的ContentProvider引证也是unstable引证
AMS层的引证计数
接着咱们来看AMS层的引证计数
AMS.refContentProvider
咱们就先从咱们刚刚剖析的ActivityThread层的引证计数修改后续:refContentProvider 看起
public boolean refContentProvider(IBinder connection, int stable, int unstable) {
ContentProviderConnection conn;
...
conn = (ContentProviderConnection)connection;
...
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的引证计数值
AMS.incProviderCountLocked
接下来咱们看AMS层引证计数的添加,AMS.incProviderCountLocked这个办法的触发机遇是在AMS.getContentProviderImpl办法中
ContentProviderConnection incProviderCountLocked(ProcessRecord r,
final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid,
String callingPackage, String callingTag, 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衔接
ContentProviderConnection conn = new ContentProviderConnection(cpr, r, callingPackage);
//建立相关
conn.startAssociationIfNeeded();
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.getCurProcState(),
cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
return conn;
}
cpr.addExternalProcessHandleLocked(externalProcessToken, callingUid, callingTag);
return null;
}
假如调用方进程已存在对应ContentProviderConnection衔接,则在其基础上添加引证计数,不然新建衔接,然后初始化引证计数值
AMS.decProviderCountLocked
然后是削减引证计数,之前在ActivityThread减引证到0后,会延时调用ActivityThread.completeRemoveProvider办法,在这个办法中会调用到AMS.removeContentProvider办法
public void removeContentProvider(IBinder connection, boolean stable) {
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
ContentProviderConnection conn = (ContentProviderConnection)connection;
...
//削减引证计数
if (decProviderCountLocked(conn, null, null, stable)) {
//更新进程优先级
updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
在这个办法中便会调用AMS.decProviderCountLocked削减引证计数,然后更新进程优先级
boolean decProviderCountLocked(ContentProviderConnection conn,
ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
if (conn != null) {
cpr = conn.provider;
//削减引证计数值
if (stable) {
conn.stableCount--;
} else {
conn.unstableCount--;
}
if (conn.stableCount == 0 && conn.unstableCount == 0) {
//停止相关
conn.stopAssociation();
//移除衔接
cpr.connections.remove(conn);
conn.client.conProviders.remove(conn);
if (conn.client.setProcState < PROCESS_STATE_LAST_ACTIVITY) {
// The client is more important than last activity -- note the time this
// is happening, so we keep the old provider process around a bit as last
// activity to avoid thrashing it.
if (cpr.proc != null) {
cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
}
}
//停止相关
stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
return true;
}
return false;
}
cpr.removeExternalProcessHandleLocked(externalProcessToken);
return false;
}
削减引证计数值,假如stable和unstable引证计数均为0,则将这个衔接移除
ContentProvider逝世杀死调用方进程的进程
咱们前面提到过,ContentProvider地点进程逝世会将与其一切有stable相关的调用方进程杀死,这是怎样做到的呢?在之前的文章中,咱们介绍过进程发动时,在调用AMS.attachApplicationLocked时会注册一个App进程逝世回调,咱们就从进程逝世,触发逝世回调开端剖析
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
...
//注册App进程逝世回调
AppDeathRecipient adr = new AppDeathRecipient(
app, pid, thread);
thread.asBinder().linkToDeath(adr, 0);
app.deathRecipient = adr;
...
}
注册了逝世回调后,假如对应binder进程逝世,便会回调IBinder.DeathRecipient.binderDied办法,咱们来看一下AppDeathRecipient对这个办法的完结
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
final IApplicationThread mAppThread;
AppDeathRecipient(ProcessRecord app, int pid,
IApplicationThread thread) {
mApp = app;
mPid = pid;
mAppThread = thread;
}
@Override
public void binderDied() {
synchronized(ActivityManagerService.this) {
appDiedLocked(mApp, mPid, mAppThread, true, null);
}
}
}
直接易手调用AMS.appDiedLocked办法,然后通过handleAppDiedLocked调用到cleanUpApplicationRecordLocked办法中
final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
...
boolean restart = false;
// Remove published content providers.
//铲除已发布的ContentProvider
for (int i = app.pubProviders.size() - 1; i >= 0; i--) {
ContentProviderRecord cpr = app.pubProviders.valueAt(i);
if (cpr.proc != app) {
// If the hosting process record isn't really us, bail out
continue;
}
final boolean alwaysRemove = app.bad || !allowRestart;
final boolean inLaunching = removeDyingProviderLocked(app, cpr, alwaysRemove);
if (!alwaysRemove && inLaunching && cpr.hasConnectionOrHandle()) {
// We left the provider in the launching list, need to
// restart it.
restart = true;
}
cpr.provider = null;
cpr.setProcess(null);
}
app.pubProviders.clear();
// Take care of any launching providers waiting for this process.
//铲除正在发动中的ContentProvider
if (cleanupAppInLaunchingProvidersLocked(app, false)) {
mProcessList.noteProcessDiedLocked(app);
restart = true;
}
// Unregister from connected content providers.
//铲除已衔接的ContentProvider
if (!app.conProviders.isEmpty()) {
for (int i = app.conProviders.size() - 1; i >= 0; i--) {
ContentProviderConnection conn = app.conProviders.get(i);
conn.provider.connections.remove(conn);
stopAssociationLocked(app.uid, app.processName, conn.provider.uid,
conn.provider.appInfo.longVersionCode, conn.provider.name,
conn.provider.info.processName);
}
app.conProviders.clear();
}
...
}
能够看到,这个办法中遍历了ProcessRecord.pubProviders,逐一对发布的ContentProvider调用removeDyingProviderLocked办法履行移除操作
private final boolean removeDyingProviderLocked(ProcessRecord proc,
ContentProviderRecord cpr, boolean always) {
boolean inLaunching = mLaunchingProviders.contains(cpr);
if (inLaunching && !always && ++cpr.mRestartCount > ContentProviderRecord.MAX_RETRY_COUNT) {
// It's being launched but we've reached maximum attempts, force the removal
always = true;
}
if (!inLaunching || always) {
synchronized (cpr) {
cpr.launchingApp = null;
cpr.notifyAll();
}
final int userId = UserHandle.getUserId(cpr.uid);
// Don't remove from provider map if it doesn't match
// could be a new content provider is starting
//移除缓存
if (mProviderMap.getProviderByClass(cpr.name, userId) == cpr) {
mProviderMap.removeProviderByClass(cpr.name, userId);
}
String names[] = cpr.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
// Don't remove from provider map if it doesn't match
// could be a new content provider is starting
//移除缓存
if (mProviderMap.getProviderByName(names[j], userId) == cpr) {
mProviderMap.removeProviderByName(names[j], userId);
}
}
}
for (int i = cpr.connections.size() - 1; i >= 0; i--) {
ContentProviderConnection conn = cpr.connections.get(i);
if (conn.waiting) {
// If this connection is waiting for the provider, then we don't
// need to mess with its process unless we are always removing
// or for some reason the provider is not currently launching.
if (inLaunching && !always) {
continue;
}
}
ProcessRecord capp = conn.client;
conn.dead = true;
if (conn.stableCount > 0) {
if (!capp.isPersistent() && capp.thread != null
&& capp.pid != 0
&& capp.pid != MY_PID) {
//当调用方与被杀死的方针ContentProvider进程间有stable衔接
//而且调用方App进程非persistent进程而且非system_server进程中的状况下
//杀死调用方进程
capp.kill("depends on provider "
+ cpr.name.flattenToShortString()
+ " in dying proc " + (proc != null ? proc.processName : "??")
+ " (adj " + (proc != null ? proc.setAdj : "??") + ")",
ApplicationExitInfo.REASON_DEPENDENCY_DIED,
ApplicationExitInfo.SUBREASON_UNKNOWN,
true);
}
} else if (capp.thread != null && conn.provider.provider != null) {
try {
//告诉调用方移除ContentProvider
capp.thread.unstableProviderDied(conn.provider.provider.asBinder());
} catch (RemoteException e) {
}
// In the protocol here, we don't expect the client to correctly
// clean up this connection, we'll just remove it.
//移除衔接
cpr.connections.remove(i);
if (conn.client.conProviders.remove(conn)) {
stopAssociationLocked(capp.uid, capp.processName, cpr.uid,
cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
}
}
}
if (inLaunching && always) {
mLaunchingProviders.remove(cpr);
cpr.mRestartCount = 0;
inLaunching = false;
}
return inLaunching;
}
能够看到,在这个办法中遍历了ContentProvider下的一切衔接,当发现有其他进程与自己建立了stable衔接(conn.stableCount > 0),且调用方进程不是persistent进程(常驻进程,只要具有体系签名的App设置这个特点才收效),也不是运转在system_server进程,调用ProcessRecord.kill办法直接杀死进程,关于没有建立stable衔接的调用方进程,调用IApplicationThread.unstableProviderDied办法告诉调用方进程移除相应的ContentProvider
所以,运用ContentProvider是有必定危险的,咱们要注意规避
总结
到这儿,整个Framework层关于ContentProvider的内容应该都剖析完了,期望咱们看完后能取得一些收获,接下来的文章应该会去剖析Service相关源码,敬请期待~
