分析源码:Android 8.0
本文主要分析调用getContentResolver方法以后,获取到ContentProvider的流程,或者说getContentResolver方法以后,和ContentProvider建立联系的流程。
在前面一篇博客 ContentResolver与ContentProvider的搭配使用 讲到了ContentResolver的使用以及对ContentProvider的访问, 那么这其中的具体流程是怎么执行的呢, 接下来就将这个过程进行具体的分析。
通常我们都是通过Context获取到ContentResolver去读取ContentProvider的数据,以下是在前一篇博客Activity中的示例代码:
...
mResolver = getContentResolver();
...
//通过ContentResolver去查询数据
Cursor cursor = mResolver.query(URI_DICT, mProjections, null, null, null);
那获取到的ContentResolver是一个什么对象呢?它又是如何跟ContentProvider进行交互的呢? 跟踪getContentResolver()的源码:
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}
返回的是 mBase.getContentResolver(), 了解过Context家族就知道,mBase其实是ContextImpl对象, 继续在ContextImpl代码中去跟进:
private final ApplicationContentResolver mContentResolver;
...
public ContentResolver getContentResolver() {
return mContentResolver;
}
mContentResolver实际上是一个ApplicationContentResolver对象,它是ContextImpl的内部类,也是在ContextImpl的构造方法中被初始化的, 所以所有的Context对象都可以获取ContentResolver。
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal) {
Preconditions.checkNotNull(uri, "uri");
// 注释1 注释1
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
long startTime = SystemClock.uptimeMillis();
ICancellationSignal remoteCancellationSignal = null;
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
remoteCancellationSignal = unstableProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
try {
qCursor = unstableProvider.query(mPackageName, uri, projection,
queryArgs, remoteCancellationSignal);
} catch (DeadObjectException e) {
// 注释2 注释2
// The remote process has died... but we only hold an unstable
// reference though, so we might recover!!! Let's try!!!!
// This is exciting!!1!!1!!!!1
unstableProviderDied(unstableProvider);
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
return null;
}
qCursor = stableProvider.query(
mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
}
if (qCursor == null) {
return null;
}
// 注释3 注释3
// Force query execution. Might fail and throw a runtime exception here.
qCursor.getCount();
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogQueryToEventLog(durationMillis, uri, projection, queryArgs);
// Wrap the cursor object into CursorWrapperInner object.
final IContentProvider provider = (stableProvider != null) ? stableProvider
: acquireProvider(uri);
final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
stableProvider = null;
qCursor = null;
return wrapper; // 注释4 注释4
} 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);
}
}
}
在上述代码中,写了4个注释:
注释1: 首先调用了acquireUnstableProvider()方法,没有获取到provider才在下面 注释2 的代码中通过acquireProvider(uri)去获取provider;
注释2: 此处的英文注释也解释了, 远程的provider所在的进程已经挂了,但是我们持有的是unstable的provider, 所以不影响我们自己的进程,现在尝试通过acquireProvider(uri)再去启动provider所在的进程,并获取provider对象;
注释3: 此处调用了一次qCursor.getCount(), 这个只是用来访问一次远程的provider,如果provider还没有正常启动起来,则此处直接会抛出RuntimeException;
注释4: 此处最终通过query方法获取到了Cursor对象,但是这个Cursor对象实际上是一个CursorWrapperInner对象;
另外,需要特别解释的是,在Android4.1之前,我们的应用程序访问了ContentProvider,但是这个ContentProvider意外挂了,这个时候我们的应用程序也将被连带杀死!这是Android处于对数据安全的考虑而做的决定,不过貌似Google也感觉这样的方式不太友好,所以在4.1以后提出了stable和unstable的概念。对于ContentResolver的query方法,我们将默认使用unstable的ContentProvider。所以注释1先调用了acquireUnstableProvider(),没获取到时注释2 才调用acquireProvider(uri),实际上这两个方法的具体实现都在ContentResolver的具体实现类,即前面说到的ApplicationContentResolver, 他们内部都是调用ActivityThread的一个方法,只是最后传入了一个 isStable的 boolean值,具体代码如下:
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
@Override
protected IContentProvider acquireProvider(Context context, String auth) {
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
以上代码已经解释了通过ContentResolver方法返回的Cursor对象, 不过还没解释清楚我们ContentResolver是如何与ContentProvider关联到一起的, 这个就的继续深挖 mMainThread.acquireProvider()这个方法了。 实际上毫无意外,Android中绝大部分跨进程通信都是通过Binder通信完成的,我们先到ActivityThread中接着跟踪:
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
// 注释1
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
ContentProviderHolder holder = null;
try {
// 注释2
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth);
return null;
}
// 注释3
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
上述代码标注的注释里面:
针对注释3,额外解释一下,每个进程,都存在一个ActivityThread对象,ActivityThread对象又可以与AMS(ActivityManagerService)进行交互,而AMS就是用来管理四大组件的服务。这里我们的调用getContentResolver方法的应用进程中,会通过ActivityThread对象去与AMS通信,然后AMS启动ContentProvider所在进程以及ContentProvider对象, 可用一个图来简单表示:
注释1很简单,没什么好讲的,注释2 会和AMS通信, 这里先结束注释2,然后再解释注释3.
这个方法会调用到AMS的getContentProvider()方法, 然后调用到getContentProviderImpl()方法
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
...
if (providerRunning) {
//如果provider已经在运行了,做相应处理
}
if (!providerRunning) {
try {
//查询PMS,得到指定的ProviderInfo
cpi = AppGlobals.getPackageManager().
resolveContentProvider(name,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
checkTime(startTime, "getContentProviderImpl: after resolveContentProvider");
} catch (RemoteException ex) {
}
}
...
if (firstClass) {
try {
//查询PMS,得到Provider所在的Applciation信息
ApplicationInfo ai = AppGlobals.getPackageManager().
getApplicationInfo(
cpi.applicationInfo.packageName,
STOCK_PM_FLAGS, userId);
ai = getAppInfoForUser(ai, userId);
//AMS内部通过ContentProviderRecord对象来保存ContentProvider
//类似于Activity使用ActivityRecord来保存,四大组件都是类似的
cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
} catch (RemoteException ex) {
}
...
if (proc != null && proc.thread != null && !proc.killed) {
//如果ContentProvider已经启动, 做相应操作
} else {
//如果未启动,则启动ConTentProvider所在进程
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
}
...
synchronized (cpr) {
while (cpr.provider == null) {
if (cpr.launchingApp == null) {
return null;
}
...
try {
cpr.wait();//等待ContentProvider进程启动完毕
} catch (InterruptedException ex) {
}...
以上代码已经将注释2解释清楚了,要去获取ContentProvider对象在本地的代理,如果ContentProvider已经启动,则可以得到IContentProvider对象,否则,需要启动ContentProvider所在进程以及ContentProvider本身。 AMS等待直到Provider的进程启动完毕。
应用进程启动后(对于应用进程启动不是很了解的可以自行查找),第一件大事就是调用AMS的attachApplication方法,ContentProvider进程自然也不例外, 然后会调用ActivityThread的bindApplication方法,最终会调用到handleBindApplication方法:
private void handleBindApplication(AppBindData data) {
try {
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
//注释 2.1, 这里会去启动当前进程的所有ContentProvider
installContentProviders(app, data.providers);
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
try {//注释 2.2 这里会去调用当前进程Applciation的onCreate方法
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
...
}
}
这个方法重点需要关注两点:
注释2.1: 这里会去启动当前进程的所有ContentProvider,只要在manifest注册了的都会启动,然后依次回调每个Provider的onCreate方法;
注释2.2: 这里会调用当前进程Application的onCreate 方法, 所以Applciation的onCreate方法调用时在ContentProvider的onCreate方法后面的;
像 为什么ContentProvider onCreate()在 Application onCreate()之前执行?为什么数据库的创建在Application onCreate()之前执行?等问题,在这处的源码里得到了解释。
而注释2.1调用的installContentProviders()方法,最终也是调用了installProvider()方法, 所以我们接着解释前面的注释3
再插入一下图片
前面解释了,调用getContentResolver方法所在的进程(以下称调用进程)也会调用installProvider()方法,而ContentProvider所在进程在启动时也会调用这个方法,就是通过包名信息来做的判断。
此方法中,如果是调用进程,则会等待ContentProvider初始化完成后,通过AMS得到本地的代理对象; 如果是ContentProvider所在进程,则会通过反射去完成初始化。
以下只贴出通过反射建立实例的代码:
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
...
if (context.getPackageName().equals(ai.packageName)) {
...
} else ...
try {
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();
if (provider == null) {
Slog.e(TAG, "Failed to instantiate class " +
info.name + " from sourceDir " +
info.applicationInfo.sourceDir);
return null;
}
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
...
}
}
最后,ContentProvider及所在进程初始化完毕后,借助AMS的publishContentProvider方法把ContentProvider信息发布出去, 这个方法就不分析了, 此时, 调用进程的getContentResolver最终得到了要找寻的 ContentProvider的本地代理对象, 和对应的ContentProvider建立了联系!
额外说明:
这里的 android:multiprocess=“false” 其实我们在manifest中通常是不会配置的,默认也是false, 这个配置的含义是, 是否建立ContentProvider的多个实例,如果为false, 只会有一个实例, 如果true,则当不同进程来调用同一个Provider时,则会建立多个实例。
ContentProvider的创建及启动流程到这里就分析完了。
喜欢的朋友麻烦点个赞吧~~
参考资料:
《深入理解Android 卷2》
https://www.jianshu.com/p/efff9a5d820f