ContentProvider的启动过程源代码分析
因为我们是通过ContentResolver来跟ContentProvider进行交互的,所以ContentProvider的启动的开始便从getContentResolver()开始分析。
1、获取ContentResolver并向ContentProvider插入一条数据
//获取ContentResolver并向ContentProvider插入一条数据
getContentResolver().insert(uri, values);
2、getContentResolver()具体实现是在ContextImpl中
//ContextImpl.java
class ContextImpl extends Context {
......
private ApplicationContentResolver mContentResolver;
......
private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
@NonNull LoadedApk packageInfo, @Nullable String splitName,
@Nullable IBinder activityToken, @Nullable UserHandle user, int flags,
@Nullable ClassLoader classLoader) {
......
mContentResolver = new ApplicationContentResolver(this, mainThread);
......
}
......
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
......
}
可以看出通过getContentResolver获得的是一个ApplicationContentResolver类型的实例。ApplicationContentResolver是ContextImpl的一个内部类,它的父类是ContentResolver。
private static final class ApplicationContentResolver extends ContentResolver {
private final ActivityThread mMainThread;
private final UserHandle mUser;
public ApplicationContentResolver(
Context context, ActivityThread mainThread, UserHandle user) {
super(context);
mMainThread = Preconditions.checkNotNull(mainThread);
mUser = Preconditions.checkNotNull(user);
}
}
ContentResolver是Android提供的一个抽象类。所以getContentResolver获取的是一个ContentResolver。
/**
* This class provides applications access to the content model.
*
* <div class=“special reference”>
* <h3>Developer Guides</h3>
* <p>For more information about using a ContentResolver with content providers, read the
* <a href=“{@docRoot}guide/topics/providers/content-providers.html”>Content Providers</a>
* developer guide.</p>
*/
public abstract class ContentResolver {
}
3、我们获取到ContentResolver之后便调用insert方法向ContentProvider插入一条数据。所以我们来看下insert方法,它是在ContentResolver中定义的
public abstract class ContentResolver {
public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
@Nullable ContentValues values) {
Preconditions.checkNotNull(url, “url”);
IContentProvider provider = acquireProvider(url);//1
if (provider == null) {
throw new IllegalArgumentException("Unknown URL " + url);
}
try {
long startTime = SystemClock.uptimeMillis();
Uri createdRow = provider.insert(mPackageName, url, values);//2
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
return createdRow;
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
} finally {
releaseProvider(provider);
}
}
}
我们可以看到它在注释1处首先调用了acquireProvider来获取一个IContentProvider实例。然后在注释2处调用获取到的IContentProvider实例的insert方法进行插入数据操作。
IContentProvider是Android提供的用来跟ContentProvider进行进程间通信的Binder接口。
public interface IContentProvider extends IInterface {
public Cursor query(String callingPkg, Uri url, @Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
throws RemoteException;
public String getType(Uri url) throws RemoteException;
public Uri insert(String callingPkg, Uri url, ContentValues initialValues)
throws RemoteException;
public int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues)
throws RemoteException;
public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
throws RemoteException;
public int update(String callingPkg, Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException;
}
继续看acquireProvider方法,在注释1处它验证了传入的uri格式是否正确,即Scheme是否为content,之后在注释2处调用同名的抽象函数,因为我们通过getContentResolver()拿到的是一个ApplicationContentResolver实例所以这个抽象的acquireProvider具体实现是在ApplicationContentResolver类中。
public final IContentProvider acquireProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {//1
return null;
}
final String auth = uri.getAuthority();
if (auth != null) {
return acquireProvider(mContext, auth);//2
}
return null;
}
protected abstract IContentProvider acquireProvider(Context c, String name);
可以看出它什么也没做只是调用了ActivityThread的acquireProvider方法。
private static final class ApplicationContentResolver extends ContentResolver {
private final ActivityThread mMainThread;
@Override
protected IContentProvider acquireProvider(Context context, String auth) {
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
}
4、ActivityThread的acquireProvider首先调用了acquireExistingProvider来查找是否已经存在要找的ContentProvider,这里我们是第一次调用所以返回为null,接着调用注释2处的AMS的getContentProvider获取一个ContentProviderHolder对象,之后还会调用installProvider函数来把这个ContentProviderHolder保存在本地,以便下次要使用这个ContentProvider接口时,直接就可以通过getExistingProvider函数获取。
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);//1
if (provider != null) {
return provider;
}
ContentProviderHolder holder = null;
try {
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), auth, userId, stable);//2
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
if (holder == null) {
Slog.e(TAG, “Failed to find provider info for “ + auth);
return null;
}
// Install provider will increment the reference count for us, and break
// any ties in the race.
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);//3
return holder.provider;
}
5、ActivityManagerService.getContentProvider,它进一步调用getContentProviderImpl。
@Override
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name, int userId, boolean stable) {
enforceNotIsolatedCaller("getContentProvider");
if (caller == null) {
String msg = “null IApplicationThread when getting content provider “
+ name;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
// The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
// with cross-user grant.
return getContentProviderImpl(caller, name, null, stable, userId);
}
6、ActivityManagerService.getContentProviderImpl,该函数很长我们分段来看。
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
synchronized(this) {
long startTime = SystemClock.uptimeMillis();
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller);//1、获取调用进程的进程记录块
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when getting content provider " + name);
}
}
boolean checkCrossUser = true;
checkTime(startTime, "getContentProviderImpl: getProviderByName");
// First check if this content provider has been published…
cpr = mProviderMap.getProviderByName(name, userId);//2、检查要获取的provider是否已存在
// If that didn’t work, check if it exists for user 0 and then
// verify that it's a singleton provider before using it.
if (cpr == null && userId != UserHandle.USER_SYSTEM) {
cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
if (cpr != null) {
cpi = cpr.info;
if (isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {
userId = UserHandle.USER_SYSTEM;
checkCrossUser = false;
} else {
cpr = null;
cpi = null;
}
}
}
boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;//3、provider是否已经在运行
if (providerRunning) {//4、provider已经在运行了
cpi = cpr.info;
String msg;
checkTime(startTime, “getContentProviderImpl: before checkContentProviderPermission");
if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
!= null) {
throw new SecurityException(msg);
}
checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");
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();
checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");
// In this case the provider instance already exists, so we can
// return it right away.
conn = incProviderCountLocked(r, cpr, token, stable);
if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_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.
checkTime(startTime, “getContentProviderImpl: before updateLruProcess");
updateLruProcessLocked(cpr.proc, false, null);
checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
}
}
checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
final int verifiedAdj = cpr.proc.verifiedAdj;
boolean success = updateOomAdjLocked(cpr.proc, true);
// 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);
checkTime(startTime, “getContentProviderImpl: after updateOomAdj");
if (DEBUG_PROVIDER) Slog.i(TAG_PROVIDER, "Adjust success: " + success);
// 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.i(TAG, "Existing provider " + cpr.name.flattenToShortString()
+ " is crashing; detaching " + r);
boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
checkTime(startTime, “getContentProviderImpl: before appDied”);
appDiedLocked(cpr.proc);
checkTime(startTime, "getContentProviderImpl: after appDied");
if (!lastRef) {
// This wasn't the last ref our process had on
// the provider... we have now been killed, bail.
return null;
}
providerRunning = false;
conn = null;
} else {
cpr.proc.verifiedAdj = cpr.proc.setAdj;
}
Binder.restoreCallingIdentity(origId);
}
if (!providerRunning) {//5、provider还未运行
try {
checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");
cpi = AppGlobals.getPackageManager().
resolveContentProvider(name,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
checkTime(startTime, "getContentProviderImpl: after resolveContentProvider");
} 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.uid, cpi.applicationInfo.uid);
if (singleton) {
userId = UserHandle.USER_SYSTEM;
}
cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
checkTime(startTime, "getContentProviderImpl: got app info for user");
String msg;
checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
!= null) {
throw new SecurityException(msg);
}
checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");
if (!mProcessesReady
&& !cpi.processName.equals("system")) {
// If this content provider does not run in the system
// process, and the system is not yet ready to run other
// processes, then fail fast instead of hanging.
throw new IllegalArgumentException(
"Attempt to launch content provider before system ready");
}
// Make sure that the user who owns this provider is running. If not,
// we don’t want to allow it to run.
if (!mUserController.isUserRunningLocked(userId, 0)) {
Slog.w(TAG, “Unable to launch app “
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
+ name + ": user " + userId + " is stopped");
return null;
}
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
checkTime(startTime, "getContentProviderImpl: before getProviderByClass");
cpr = mProviderMap.getProviderByClass(comp, userId);//6、通过Classname 查找是否存在对应的provider
checkTime(startTime, "getContentProviderImpl: after getProviderByClass");
final boolean firstClass = cpr == null;//7、没有找到对应的provider说明这是首次启动
if (firstClass) {
final long ident = Binder.clearCallingIdentity();
// If permissions need a review before any of the app components can run,
// we return no provider and launch a review activity if the calling app
// is in the foreground.
if (mPermissionReviewRequired) {
if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) {
return null;
}
}
try {
checkTime(startTime, “getContentProviderImpl: before getApplicationInfo”);
ApplicationInfo ai =
AppGlobals.getPackageManager().
getApplicationInfo(
cpi.applicationInfo.packageName,
STOCK_PM_FLAGS, userId);
checkTime(startTime, "getContentProviderImpl: after getApplicationInfo");
if (ai == null) {
Slog.w(TAG, "No package info for content provider "
+ cpi.name);
return null;
}
ai = getAppInfoForUser(ai, userId);
cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);//8、创建对应的ContentProviderRecord
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
} finally {
Binder.restoreCallingIdentity(ident);
}
}
checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord");
if (r != null && cpr.canRunHere(r)) {
// If this is a multiprocess provider, then just return its
// info and allow the caller to instantiate it. Only do
// this if the provider is the same user as the caller's
// process, or can run as root (so can be in any process).
return cpr.newHolder(null);
}
if (DEBUG_PROVIDER) Slog.w(TAG_PROVIDER, "LAUNCHING REMOTE PROVIDER (myuid "
+ (r != null ? r.uid : null) + “ pruid “ + cpr.appInfo.uid + “): “
+ cpr.info.name + “ callers=“ + Debug.getCallers(6));
// This is single process, and our app is now connecting to it.
// See if we are already in the process of launching this
// provider.
//9、系统中所有正在加载的Content Provider都保存在mLaunchingProviders成员变量中。
// 在加载相应的Content Provider之前,首先要判断一下它是否正在被其它应用程序加载,如果是的话,就不用重复加载了
final int N = mLaunchingProviders.size();
int i;
for (i = 0; i < N; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
// If the provider is not already being launched, then get it
// started.
//10、该ContentProvider没有被其它应用程序加载
if (i >= N) {
final long origId = Binder.clearCallingIdentity();
try {
// Content provider is now in use, its package can’t be stopped.
try {
checkTime(startTime, "getContentProviderImpl: before set stopped state");
AppGlobals.getPackageManager().setPackageStoppedState(
cpr.appInfo.packageName, false, userId);
checkTime(startTime, "getContentProviderImpl: after set stopped state");
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, “Failed trying to unstop package “
+ cpr.appInfo.packageName + “: “ + e);
}
// Use existing process if already started
checkTime(startTime, “getContentProviderImpl: looking for process record");
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null && !proc.killed) {
if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,
"Installing in existing process " + proc);
if (!proc.pubProviders.containsKey(cpi.name)) {
checkTime(startTime, "getContentProviderImpl: scheduling install");
proc.pubProviders.put(cpi.name, cpr);
try {
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
} else {
checkTime(startTime, "getContentProviderImpl: before start process");
//11、启动一个新的进程来加载这个ContentProvider对应的类
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
checkTime(startTime, "getContentProviderImpl: after start process");
if (proc == null) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + “ for provider “
+ name + “: process is bad”);
return null;
}
}
cpr.launchingApp = proc;
//12、把加载的ContentProvider加到mLaunchingProviders中
mLaunchingProviders.add(cpr);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
checkTime(startTime, "getContentProviderImpl: updating data structures");
// Make sure the provider is published (the same provider class
// may be published under multiple names).
//13、把加载的ContentProvider的信息分别保存到mProvidersByName和mProviderByCalss两个Map中去,以方便后续查询
if (firstClass) {
mProviderMap.putProviderByClass(comp, cpr);
}
mProviderMap.putProviderByName(name, cpr);
conn = incProviderCountLocked(r, cpr, token, stable);
if (conn != null) {
conn.waiting = true;
}
}
checkTime(startTime, “getContentProviderImpl: done!”);
grantEphemeralAccessLocked(userId, null /*intent*/,
cpi.applicationInfo.uid, UserHandle.getAppId(Binder.getCallingUid()));
}
// Wait for the provider to be published…
//14、因为我们需要获取的ContentProvider是在新的进程中加载的,而getContentProviderImpl这个函数是在系统进程中执行的,
// 它必须要等到要获取的ContentProvider在新的进程中加载完成后才能返回,这样就涉及到进程同步的问题了。
// 这里使用的同步方法是不断地去检查变量cpr的provider域是否被设置了。当要获取的ContentProvider在新的进程加载完成之后,
// 它会通过Binder进程间通信机制调用到系统进程中,把这个cpr变量的provider域设置为已经加载好的ContentProvider接口,
// 这时候函数getContentProviderImpl就可以返回了
synchronized (cpr) {
while (cpr.provider == null) {
if (cpr.launchingApp == null) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
+ name + ": launching app became null");
EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS,
UserHandle.getUserId(cpi.applicationInfo.uid),
cpi.applicationInfo.packageName,
cpi.applicationInfo.uid, name);
return null;
}
try {
if (DEBUG_MU) Slog.v(TAG_MU,
"Waiting to start provider " + cpr
+ " launchingApp=" + cpr.launchingApp);
if (conn != null) {
conn.waiting = true;
}
cpr.wait();
} catch (InterruptedException ex) {
} finally {
if (conn != null) {
conn.waiting = false;
}
}
}
}
return cpr != null ? cpr.newHolder(conn) : null;
}
首先看注释1处获取了调用进程的进程记录块,方便后面使用
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller);//1、获取调用进程的进程记录块
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ “) when getting content provider “ + name);
}
}
在注释2处检查是否已经存在想要获取的provider
cpr = mProviderMap.getProviderByName(name, userId);//2、检查要获取的provider是否已存在
在注释3处根据上边的查找情况判断provider是否已经在运行了,因为这是首次启动所以providerRunning为false。
boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;//3、provider是否已经在运行
如果还未运行那么会进入注释5的分支,之后在注释6处通过classname 查找是否存在对应的provider,在注释2处也查找过否存在对应的provider。这里解释下因为有两个成员变量用来保存系统中的Content Provider信息的,一个是mProvidersByName,一个是mProvidersByClass,前者是以Content Provider的authoriry值为键值来保存的,后者是以Content Provider的类名为键值来保存的。一个Content Provider可以有多个authority,而只有一个类来和它对应,因此,这里要用两个Map来保存,为了方便根据不同条件来快速查找而设计的。如果仍未找到那么就说明这是首次加载,在注释8处创建对应的ContentProviderRecord。
if (!providerRunning) {//5、provider还未运行
//. . .
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
checkTime(startTime, “getContentProviderImpl: before getProviderByClass”);
cpr = mProviderMap.getProviderByClass(comp, userId);//6、通过Classname 查找是否存在对应的provider
checkTime(startTime, “getContentProviderImpl: after getProviderByClass”);
final boolean firstClass = cpr == null;//7、没有找到对应的provider说明这是首次启动
if (firstClass) {
final long ident = Binder.clearCallingIdentity();
//. . .
cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);//8、创建对应的ContentProviderRecord
}
注释9系统中所有正在加载的Content Provider都保存在mLaunchingProviders成员变量中。在加载相应的Content Provider之前,首先要判断一下它是否正在被其它应用程序加载,如果是的话,就不用重复加载了。
// This is single process, and our app is now connecting to it.
// See if we are already in the process of launching this
// provider.
//9、系统中所有正在加载的Content Provider都保存在mLaunchingProviders成员变量中。
// 在加载相应的Content Provider之前,首先要判断一下它是否正在被其它应用程序加载,如果是的话,就不用重复加载了
final int N = mLaunchingProviders.size();
int i;
for (i = 0; i < N; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
下面就是如果ContentProvider没有被其它应用程序加载那么会启动一个新的进程去加载,完成后把其加到mLaunchingProviders中
//10、该ContentProvider没有被其它应用程序加载
if (I >= N) {
//. . .
//11、启动一个新的进程来加载这个ContentProvider对应的类
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, “content provider”,
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
//. . .
//12、把加载的ContentProvider加到mLaunchingProviders中
mLaunchingProviders.add(cpr);
}
接着把加载的ContentProvider的信息分别保存到mProvidersByName和mProviderByCalss两个Map中去,以方便后续查询
//13、把加载的ContentProvider的信息分别保存到mProvidersByName和mProviderByCalss两个Map中去,以方便后续查询
if (firstClass) {
mProviderMap.putProviderByClass(comp, cpr);
}
mProviderMap.putProviderByName(name, cpr);
因为我们需要获取的ContentProvider是在新的进程中加载的,而getContentProviderImpl这个函数是在系统进程中执行的,它必须要等到要获取的ContentProvider在新的进程中加载完成后才能返回,这样就涉及到进程同步的问题了。这里使用的同步方法是不断地去检查变量cpr的provider域是否被设置了。当要获取的ContentProvider在新的进程加载完成之后,
它会通过Binder进程间通信机制调用到系统进程中,把这个cpr变量的provider域设置为已经加载好的ContentProvider接口,这时候函数getContentProviderImpl就可以返回了
// Wait for the provider to be published…
//14、因为我们需要获取的ContentProvider是在新的进程中加载的,而getContentProviderImpl这个函数是在系统进程中执行的,
// 它必须要等到要获取的ContentProvider在新的进程中加载完成后才能返回,这样就涉及到进程同步的问题了。
// 这里使用的同步方法是不断地去检查变量cpr的provider域是否被设置了。当要获取的ContentProvider在新的进程加载完成之后,
// 它会通过Binder进程间通信机制调用到系统进程中,把这个cpr变量的provider域设置为已经加载好的ContentProvider接口,
// 这时候函数getContentProviderImpl就可以返回了
synchronized (cpr) {
while (cpr.provider == null) {
try {
if (DEBUG_MU) Slog.v(TAG_MU,
“Waiting to start provider “ + cpr
+ “ launchingApp=“ + cpr.launchingApp);
if (conn != null) {
conn.waiting = true;
}
cpr.wait();
} catch (InterruptedException ex) {
}
}
}
以上就是整个getContentProviderImpl的操作。
7、上面我们知道要获取的ContentProvider还未加载,需要启动一个新的进程去加载。新进程的启动时通过startProcessLocked来完成的,具体的启动过程这里就不具体展开了。当新进程启动完毕之后会依次调用这几个函数ActivityThread.main——> ActivityThread.attach——>ActivityManagerService.attachApplication。我们从attachApplication开始分析。
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
//此处我们只看跟ContentProvider有关的内容
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;//1、调用generateApplicationProvidersLocked获得需要在这个过程中加载的Content Provider列表
//. .
//2、调用bindApplication执行一些应用程序初始化工作
thread.bindApplication(processName, appInfo, providers,
app.instr.mClass,
profilerInfo, app.instr.mArguments,
app.instr.mWatcher,
app.instr.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial);
}
8、我们先看generateApplicationProvidersLocked
private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
List<ProviderInfo> providers = null;
try {
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) {
}
}
它是通过AppGlobals.getPackageManager().queryContentProviders().getList()来获取要加载的ContentProvider。
9、然后我们接着看bindApplication,它的第三个参数就是我们在generateApplicationProvidersLocked中获取的要加载的ContentProvider list。
public final void bindApplication(String processName, ApplicationInfo appInfo,
List<ProviderInfo> providers, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
String buildSerial) {
if (services != null) {
// Setup the service cache in the ServiceManager
ServiceManager.initServiceCache(services);
}
setCoreSettings(coreSettings);
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providers;
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableBinderTracking = enableBinderTracking;
data.trackAllocation = trackAllocation;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
data.buildSerial = buildSerial;
sendMessage(H.BIND_APPLICATION, data);
}
它的操作就是把相关的信息都封装成一个AppBindData对象,然后以一个消息的形式发送到主线程的消息队列中等待处理。
10、ActivityThread中处理BIND_APPLICATION消息的是handleBindApplication函数,该函数很长 我们只关注跟ContentProvider有关的操作,它取出了AppBindData中的provider然后调用installContentProviders。
private void handleBindApplication(AppBindData data)
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);
// For process that contains content providers, we want to
// ensure that the JIT is enabled "at some point".
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
11、installContentProviders
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
//1、installProvider来在本地安装每一个ContentProivder的信息
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 {
//2、调用ActivityManagerService服务的publishContentProviders函数来通知ActivityManagerService服务,这个进程中所要加载的Content Provider,都已经准备完毕
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
我们看到在注释1处调用installProvider来在本地安装每一个Content Proivder的信息,并且为每一个Content Provider创建一个ContentProviderHolder对象来保存相关的信息。ContentProviderHolder对象是一个Binder对象,是用来把Content Provider的信息传递给ActivityManagerService服务的。然后在注释2处通过publishContentProviders函数来通知ActivityManagerService服务,这个进程中所要加载的Content Provider,都已经准备完毕。publishContentProviders函数的作用就是用来唤醒在前面步骤6中等待的线程
12、我们先来看installProvider
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
//. . .
try {
//1、加载ContentProvider
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
}
//. . .
//2、保存ContentProvider
synchronized (mProviderMap) {
IBinder jBinder = provider.asBinder();
if (localProvider != null) {
ComponentName cname = new ComponentName(info.packageName, info.name);
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
if (pr != null) {
provider = pr.mProvider;
} else {
holder = new ContentProviderHolder(info);
holder.provider = provider;
holder.noReleaseNeeded = true;
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
}
retHolder = pr.mHolder;
//. . .
}
installProvider主要做了两件事在注释1处加载ContentProvider并调用getIContentProvider函数来获得一个Binder对象,这个Binder对象返回给installContentProviders函数之后,就会传到ActivityManagerService中去,后续其它应用程序就是通过获得这个Binder对象来和相应的Content Provider进行通信,然后又调用attachInfo
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();
localProvider.attachInfo(c, info);
attachInfo最终会调用ContentProvider的onCreate函数,这样ContentProvider就完成了创建加载。
public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
mNoPerms = testing;
/*
* Only allow it to be set once, so after the content service gives
* this to us clients can't change it.
*/
if (mContext == null) {
mContext = context;
if (context != null) {
mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
Context.APP_OPS_SERVICE);
}
mMyUid = Process.myUid();
if (info != null) {
setReadPermission(info.readPermission);
setWritePermission(info.writePermission);
setPathPermissions(info.pathPermissions);
mExported = info.exported;
mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
setAuthorities(info.authority);
}
ContentProvider.this.onCreate();//回调onCreate
}
}
ContentProvider加载完成之后,会在注释2处把创建的ContentProvider保存起来。
synchronized (mProviderMap) {
IBinder jBinder = provider.asBinder();
if (localProvider != null) {
ComponentName cname = new ComponentName(info.packageName, info.name);
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
if (pr != null) {
provider = pr.mProvider;
} else {
holder = new ContentProviderHolder(info);
holder.provider = provider;
holder.noReleaseNeeded = true;
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
}
retHolder = pr.mHolder;
可以看分别保存到了mLocalProviders(以ContentProvider对应的Binder对象provider为键值来保存,表明这是一个在本地加载的ContentProvider)、 mLocalProvidersByName(以ContentProvider类名为键值保存)和mProviderMap(通过installProviderAuthoritiesLocked保存到mProviderMap,以ContentProvider的author为键值保存)
下面贴下installProviderAuthoritiesLocked方法
ContentProvider localProvider, ContentProviderHolder holder) {
final String auths[] = holder.info.authority.split(“;”);
final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);
if (provider != null) {
// If this provider is hosted by the core OS and cannot be upgraded,
// then I guess we're okay doing blocking calls to it.
for (String auth : auths) {
switch (auth) {
case ContactsContract.AUTHORITY:
case CallLog.AUTHORITY:
case CallLog.SHADOW_AUTHORITY:
case BlockedNumberContract.AUTHORITY:
case CalendarContract.AUTHORITY:
case Downloads.Impl.AUTHORITY:
case "telephony":
Binder.allowBlocking(provider.asBinder());
}
}
}
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);//以ContentProvider的author为键值保存该ContentProvider,因为可能存在多个author所以这里采用for循环
}
}
return pcr;
}
至此 installProvider执行完毕,回到步骤11然后执行其注释2处逻辑
13、步骤11注释2执行了publishContentProviders来通知ActivityManagerService服务,进程中所要加载的ContentProvider,都已经准备完毕。
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
for (int I = 0; I < N; I++) {
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
continue;
}
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (dst != null) {
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
mProviderMap.putProviderByClass(comp, dst);//1保存ContentProvider
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.putProviderByName(names[j], dst);//2保存ContentProvider
}
int launchingCount = mLaunchingProviders.size();
int j;
boolean wasInLaunchingProviders = false;
for (j = 0; j < launchingCount; j++) {
if (mLaunchingProviders.get(j) == dst) {//3把刚刚加载的ContentProvider从mLaunchingProviders移除
mLaunchingProviders.remove(j);
wasInLaunchingProviders = true;
j--;
launchingCount--;
}
}
if (wasInLaunchingProviders) {
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
synchronized (dst) {//4唤醒等待线程
dst.provider = src.provider;
dst.proc = r;
dst.notifyAll();
}
updateOomAdjLocked(r, true);
maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
src.info.authority);
}
}
}
首先在注释1和2处把ContentProvider保存,有人可能疑惑了上一步不是保存了,这里要注意的的上一步的保存是在启动ContentProvider的进程中进行的,这里保存是在AMS进程中。之后在注释3处把刚刚加载的ContentProvider从mLaunchingProviders移除。最后在注释4处唤醒等待的线程,这里指的是步骤6中注释14处等待的线程。唤醒之后,它检查本地ContentProviderRecord变量cpr的provider域不为null,于是就返回了。最终返回到步骤4的注释3处继续执行。
14、步骤4的注释3处调用了ActivityThread的installProvider,此次调用是在AMS请求调用ContentProvider的进程中。
//Step 4
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);//3
因为这次holder.provider不为空所以不需要再次加载ContentProvider
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
if (holder == null || holder.provider == null){
}else {
provider = holder.provider;
}
}
14、之后一路返回最终返回到步骤3的注释1处,然后继续执行在注释2处利用返回的provider调用其insert完成数据插入操作。至此整个ContentProvider启动过程分析完毕。
参考链接: