赞
踩
分析源码: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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。