开篇
本篇以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
相关源码,敬请期待~