本篇以android-11.0.0_r25作为基础解析
在四大组件中,可能我们平时用到最少的便是ContentProvider了,ContentProvider是用来帮助应用管理其自身和其他应用所存储数据的访问,并提供与其他应用共享数据的方法,使用ContentProvider可以安全的在应用之间共享和修改数据,比如说访问图库,通讯录等
在之前的文章中,我们提到了ContentProvider的启动时机,不妨顺水推舟,干脆把这一块分析个明白,本篇文章并不会教大家怎样使用ContentProvider,只将精力集中在ContentProvider在系统层面的启动与交互上
想要通过ContentProvider访问应用数据,我们通常需要借助ContentResolver的API,我们可以通过Context.getContentResolver方法获取其实例对象
ContentResolver是一个抽象类,它的抽象方法由ContextImpl.ApplicationContentResolver继承实现,我们实际上获取到的也是这个实例对象
ContentProvider的使用需要先获得提供者的Uri,它的格式如下:
content://AndroidManifest里设置的android:authorities属性其中,资源相对路径和资源ID不是必须的,要看资源存储的数量及形式
举个栗子,外部存储中某张图片的Uri为:content://media/external/images/media/${id},其中media为Authority,/external/images/media为外部存储图片的相对路径,id为这张图片资源在数据库中存储的id
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方法,获取qCursorquery过程中远程对象死亡,尝试获取stableProvider并调用query方法获取qCursorstableProvider(如果之前没获取的话)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);
}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对象ContentProviderIContentProvider对象我们首先看通过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获取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:
ContentProvider组件设置了单例flag(android:singleUser)且App拥有INTERACT_ACROSS_USERS权限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);
}
...
}
...
}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方法安装启动ContentProviderContentProvider所在进程未启动:启动目标进程,目标进程启动过程中会自动安装启动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
由于这个方法同时包含了启动安装本地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至关重要
在这个方法中会调用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启动超时检测
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方法中,那我们接下来就好好分析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,最后一起发布
我们先看installProvider方法,我们在上一章中分析到,获取ContentProvider的时候也会调用这个方法,这次我们就结合起来一起分析
通过上文的代码,我们发现,有两处地方会调用installProvider方法,方法的入参有三种形式,分别为:
我们将这三种情况分成两种case分别分析
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的安装过程也到此结束
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的安装也结束了
接下来我们再简单的看一下两种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跨进程操作了
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死亡杀死调用方进程的过程做准备
ActivityThread层的引用计数是和AMS层的引用计数分开的,ActivityThread记录的是目标ContentProvider在本进程中有多少处正在使用,而AMS记录的是目标ContentProvider正在被多少个进程使用
我们先从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层的引用计数
我们就先从我们刚刚分析的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层引用计数的增加,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连接,则在其基础上增加引用计数,否则新建连接,然后初始化引用计数值
然后是减少引用计数,之前在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所在进程死亡会将与其所有有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是有一定风险的,大家要注意规避
阅读量:1955
点赞量:0
收藏量:0