Android ContentProvider启动流程源码解析(8.0)
2017-12-02 00:19
363 查看
一,写在前面
在文章 Android 如何自定义一个ContentProvider中,介绍了如何使用以及自定义一个ContentProvider,本篇不再介绍如何使用ContentProvider访问其他应用的数据。在阅读本篇文章前,建议先了解Activity的启动流程,可参考文章 AndroidActivity的启动流程源码解析(8.0) ,将不再对重复的代码细节进行分析。
二,启动ContentProvider的入口
如何启动一个ContentProvider呢?在一个Activity类中,可以调用getContentResolver().query(...)查询指定uri对应的数据,在这个过程中若ContentProvider没有启动,则会启动ContentProvider。当然,相应的insert,delete,update方法也可以启动ContentProvider,本篇文章以query方法为例进行分析。获取ContentResolver的对象,实际上调用的是ContextWrapper$getContentResolver方法,ContextWrapper是抽象类Context的一个子类。
查看ContextWrapper$getContentResolver方法源码:
@Override public ContentResolver getContentResolver() { return mBase.getContentResolver(); }第3行,变量mBase对应的是ContextImpl对象,分析见Android Activity的启动流程源码解析(8.0) ,这里不再重复阐述。
查看ContextImpl$getContentResolver方法源码:
@Override public ContentResolver getContentResolver() { return mContentResolver; }第3行,变量mContentResolver的初始化在ContextImpl的构造函数中完成,mContentResolver = new ApplicationContentResolver(this, mainThread, user),也就是返回的是ApplicationContentResolver的实例。值得一提的是,ContextImpl对象的创建是在启动Activity的流程中完成,具体分析见 Android
Activity的启动流程源码解析(8.0) ,这里不再重复阐述。
ApplicationContentResolver是ContextImpl的一个内部类,且继承类ContentResolver。调用getContentResolver().query(...)方法,是从父类ContentResolver继承的query方法。
查看ContentResolver$query方法源码:
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) { //... IContentProvider unstableProvider = acquireUnstableProvider(uri); if (unstableProvider == null) { return null; } //... qCursor = unstableProvider.query(mPackageName, uri, projection, selection, selectionArgs, sortOrder, remoteCancellationSignal); //... }第8行,获取一个IContentProvider类型的对象unstableProvider。启动ContentProvider从这里开始分析,后面会详细分析。
第9行,对变量unstableProvider进行判空检查;
第15行,根据指定uri,执行查询数据操作,文章的最后会详细分析。
查看ContentResolver$acquireUnstableProvider方法(一个参数)源码:
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; }第2行,对uri的scheme进行检查;
第7行,逻辑跳到ApplicationContentResolver$acquireUnstableProvider方法(两个参数);
查看ApplicationContentResolver$acquireUnstableProvider方法源码:
@Override protected IContentProvider acquireUnstableProvider(Context c, String auth) { return mMainThread.acquireProvider(c, ContentProvider.getAuthorityWithoutUserId(auth), resolveUserIdFromAuthority(auth), false); }第3行,变量mMainThread是一个ActivityThread类型的对象,该变量的初始化也是在ContextImpl的构造函数中完成。
查看ActivityThread$acquireProvider方法源码:
public final IContentProvider acquireProvider( Context c, String auth, int userId, boolean stable) { final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable); if (provider != null) { return provider; } ContentProviderHolder holder = null; //... holder = ActivityManager.getService().getContentProvider( getApplicationThread(), auth, userId, stable); //... holder = installProvider(c, holder, holder.info, true /*noisy*/, holder.noReleaseNeeded, stable); return holder.provider; }第3行,调用ActivityThread$acquireExistingProvider方法,查询Map集合中是否包含该IContextProvider对象,下面会具体分析。
第4行,若provider不为null,表示该IContextProvider对象已经存在,无需再创建。
第11行,若provider为null,那么需要创建一个IContextProvider对象。这里就是启动ContentProvider流程的入口,后面会重点具体分析。
第16行,将11行创建的IContextProvider对象存储在一个Map集合中,起到一个缓存的作用,不用每次创建IContextProvider对象。
分析ActivityThread$acquireProvider方法的第3行,查看ActivityThread$acquireExistingProvider方法源码:
public final IContentProvider acquireExistingProvider( Context c, String auth, int userId, boolean stable) { synchronized (mProviderMap) { final ProviderKey key = new ProviderKey(auth, userId); final ProviderClientRecord pr = mProviderMap.get(key); if (pr == null) { return null; } IContentProvider provider = pr.mProvider; //... return provider; } }第4行,将参数auth封装在ProviderKey对象中,并作为Map集合的key;
第5行,变量mProviderMap的初始化:final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<ProviderKey, ProviderClientRecord>();当Map集合中不存在该key时,就会返回null。
三,启动ContentProvider,交给AMS处理
前面已经提到,ActivityThread$acquireProvider方法的第11行是启动ContentProvider的入口。第11行代码:holder = ActivityManager.getService().getContentProvider(getApplicationThread(), auth, userId, stable)。ActivityManager.getService()这个已经再熟悉不过了,基于Binder机制,最终会调用系统服务ActivityManagerService$getContentProvider方法。具体分析见文章 Android
Activity的启动流程源码解析(8.0) ,这里不再重复阐述。
查看ActivityManagerService$getContentProvider方法源码:
@Override public final ContentProviderHolder getContentProvider( IApplicationThread caller, String name, int userId, boolean stable) { //... return getContentProviderImpl(caller, name, null, stable, userId); } //继续查看... private ContentProviderHolder getContentProviderImpl(IApplicationThread caller, String name, IBinder token, boolean stable, int userId) { //... ProcessRecord proc = getProcessRecordLocked( cpi.processName, cpr.appInfo.uid, false); if (proc != null && proc.thread != null && !proc.killed) { if (!proc.pubProviders.containsKey(cpi.name)) { //... proc.thread.scheduleInstallProvider(cpi); //... } } else { //... proc = startProcessLocked(cpi.processName, cpr.appInfo, false, 0, "content provider", new ComponentName(cpi.applicationInfo.packageName, cpi.name), false, false, false); //... } //...code }第17行,ProcessRecord封装了ContentProvider所在应用程序进程的相关信息;
第25行,若应用程序进程已经启动,则调用scheduleInstallProvider方法,启动ContentProvider;
第33行,若应用程序进程没有启动,则调用AMS$startProcessLocked方法启动进程。本篇文章假设应用程序进程没有启动,来研究ContentProvider的启动流程,下面会重点分析这里。
四,入口ActivityThread$main的分析
为什么要分析应用程序进程的启动呢?因为应用程序进程启动后,接着启动ContentProvider。进程启动是交给ActivityManagerService$startProcessLocked方法来处理,最终会调用ActivityThread$main方法(ActivityThread是代表主线程的实例)。这里推荐一篇博客 Android应用程序进程启动过程 ,文章详细讲解了进程的启动流程。下面继续分析ActivityThread$main方法,这个方法很重要。想要完全理解Handler机制,了解main方法也是必须的,下面会讲述一些篇外话。
ActivityThread$main方法查看源码:
public static void main(String[] args) { //... Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } //... Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }第5行,创建Looper对象,并将该对象放入ThreadLocal中。
简单介绍下ThreadLocal:可以使某一个变量在不同线程代表不同的值。在这里用于存放主线程ActivityThread中创建的Looper,当然子线程也可以创建自己的Looper,但不同线程的Looper并不是一个对象。
值得一提的:调用Looper.prepareMainLooper()方法专门用于创建主线程的Looper对象,在子线程中创建Looper对象也比较熟悉了,一般调用Looper.prepare()方法。当然这两种方式创建Looper是有区别,区别在于主线程的Looper不被允许停掉,具体代码细节分析留给感兴趣哥们验证~
第7行,创建ActivityThread的实例;
第8行,调用ActivityThread$attach方法,内部完成ContentProvider的启动过程,后面会重点分析。
第16行,进行消息循环的操作,内部有一个无限for循环,不断的获取Message对象且处理消息。
第18行,该行代码被执行的前提是:Looper$loop方法跳出无限for循环。因此需要调用Looper$quit方法停掉Looper,前面已经讲到主线程的Looper不会被停掉。由于不是本篇重点,具体的代码细节不做阐述,这里直接给出结论。也就是说,抛出RuntimeException这个异常还是比较少见的,Android系统也不支持这样做。
回到ActivityThread$attach方法的调用,它会启动ContentProvider,下面继续分析。
查看ActivityThread$attach方法源码:
private void attach(boolean system) { //... final IActivityManager mgr = ActivityManager.getService(); //... mgr.attachApplication(mAppThread); //... }第5行,返回一个IActivityManager接口的代理对象,这个应该比较熟悉了,具体分析可以参考文章 Android Activity的启动流程源码解析(8.0) ;
第9行,向系统服务AMS发起请求,基于Binder机制,回调用ActivityManagerService$attachApplication方法;变量mAppThread是一个ApplicationThread类型的变量,这个也比较熟悉了,具体分析可以参考文章 Android Activity的启动流程源码解析(8.0) ;
查看ActivityManagerService相关方法的源码:
public final void attachApplication(IApplicationThread thread) { synchronized (this) { //... attachApplicationLocked(thread, callingPid); //... } } //继续查看... private final boolean attachApplicationLocked(IApplicationThread thread, int pid) { //... 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); //... }第17行,变量thread是一个ApplicationThread的对象,调用bindApplication方法完成一次IPC的调用,代码逻辑切换到ApplicationThread中去执行。这个应该比较熟悉了,具体分析可以参考文章 Android Activity的启动流程源码解析(8.0) ,这里不再重复阐述。
查看ActivityThread$ApplicationThread$bindApplication方法源码:
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) { //... 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); }第13行,创建AppBindData对象,用于封装一些数据。
第30行,ActivityThread$sendMessage方法的内部代码不再展示。具体来说,使用Handler发送了一个消息,基于Handler的消息机制,将任务切换到Handler所在的线程中执行,于是任务切换到ActivityThread中执行。
我们会发现,Android四大组件的启动,代码逻辑由系统服务AMS切换到ApplicationThread,然后基于Handler的消息机制,最终切换到主线程ActivityThread中执行。
继续分析消息的处理过程,查看ActivityThread$H$handleMessage方法源码:
public void handleMessage(Message msg) { switch (msg.what) { //... case BIND_APPLICATION: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication"); AppBindData data = (AppBindData)msg.obj; handleBindApplication(data); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; //... } }第9行,调用ActivityThread$handleBindApplication方法;
查看ActivityThread$handleBindApplication方法源码:
private void handleBindApplication(AppBindData data) { //... final ContextImpl instrContext = ContextImpl.createAppContext(this, pi); //.. final ClassLoader cl = instrContext.getClassLoader(); mInstrumentation = instantiate(cl, data.instrumentationName.getClassName(), instrContext, Application::instantiateInstrumentation); //.. Application app = data.info.makeApplication(data.restrictedBackupMode, null); //... installContentProviders(app, data.providers); //... mInstrumentation.callApplicationOnCreate(app); //... }handleBindApplication做的事情,有些跟启动Activity的流程比较类似,类似的代码细节就不再重复分析了。
第5行,创建上下文环境,具体来说是创建ContextImpl对象;
第10行,获取Instrumentation类型的对象;
第15行,创建Application对象,由于第2个参数为null,不会调用Application$onCreate方法;
第19行,调用ctivityThread$installContentProviders方法,启动ContentProvider,后面会重点分析;
第23行,Instrumentation$callApplicationOnCreate方法内部,会调用Application$onCreate方法,意味着应用程序进程启动完毕。
查看ActivityThread$installContentProviders方法源码:
private void installContentProviders( Context context, List<ProviderInfo> providers) { final ArrayList<ContentProviderHolder> results = new ArrayList<>(); for (ProviderInfo cpi : providers) { if (DEBUG_PROVIDER) { StringBuilder buf = new StringBuilder(128); buf.append("Pub "); buf.append(cpi.authority); buf.append(": "); buf.append(cpi.name); Log.i(TAG, buf.toString()); } ContentProviderHolder cph = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/); //... } //... }第2行,集合providers中存储了应用程序进程中所有的ProviderInfo对象,ProviderInfo封装了ContentProvider相关的信息。
第5行,遍历集合providers;
第14行,调用ActivityThread$installProvider方法,ProviderInfo对象作为方法的参数,启动ContentProvider;
查看ActivityThread$installProvider方法源码:
private ContentProviderHolder installProvider(Context context, ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) { ContentProvider localProvider = null; IContentProvider provider; //.. final java.lang.ClassLoader cl = c.getClassLoader(); localProvider = instantiate(cl, info.name, context, Application::instantiateProvider); //... localProvider.attachInfo(c, info); //... }第10行,通过ClassLoader创建ContentProvider的实例;
第15行,调用ContentProvider$attachInfo方法启动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) { // ... ContentProvider.this.onCreate(); } }第15行,哇,调用了ContentProvider$onCreate方法,意味着ContentProvider已经启动了。
五,ContentProvider启动后,分析query的流程
前面分析了ContentProvider的启动流程,下面继续分析query的流程。回到ContentResolver$query方法,该方法内部调用了IContentProvider$query方法。查看IContentProvider源码:
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; //... }原来,IContentProvider是一个接口,还继承了IInterface接口。有意思的是,在AIDL文件自动生成的java文件中,可以找到一样的结构。但这里并没有类似于Stub的类,在源码中search "extends Binder implements IContentProvider",这个相当于Stub的类就是ContentProviderNative。
查看ContentProviderNative源码:
abstract public class ContentProviderNative extends Binder implements IContentProvider { public ContentProviderNative() { attachInterface(this, descriptor); } static public IContentProvider asInterface(IBinder obj) { if (obj == null) { return null; } IContentProvider in = (IContentProvider)obj.queryLocalInterface(descriptor); if (in != null) { return in; } return new ContentProviderProxy(obj); } @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { try { switch (code) { case QUERY_TRANSACTION: { //... Cursor cursor = query(callingPkg, url, projection, queryArgs, cancellationSignal); //...code } return super.onTransact(code, data, reply, flags); } //...code @Override public IBinder asBinder() { return this; } final class ContentProviderProxy implements IContentProvider { public ContentProviderProxy(IBinder remote) { mRemote = remote; } @Override public IBinder asBinder() { return mRemote; } @Override public Cursor query(String callingPkg, Uri url, @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) throws RemoteException { BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor(); Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); //...code mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); //...code } //...code } //...code }哇~这个结构是不是似曾相识,类ContentProviderNative相当于Stub类。接下来,需要找到接口的实现类,源码中search "extends ContentProviderNative ",这个实现类就是ContentProvider的内部类Transport。
查看ContentProvider$Transport$query源码:
class Transport extends ContentProviderNative { //... @Override public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) { //... return ContentProvider.this.query( uri, projection, queryArgs, CancellationSignal.fromTransport(cancellationSignal)); //... } //... }第11行,哇,最终调用了ContentProvider的query方法,这个不就是我们想要的嘛。这也验证访问ContentProvider的数据,是基于Binder机制完成了一次进程间的通信。
值得一提的是理解这个流程,需要对AIDL文件生成的java文件有比较好的认识,由于不是本篇重点,这里不进行阐述。
六,最后
本篇文章分两部分进行,前面一部分,用大量篇幅分析ContentProvider的启动过程,这也是本篇文章的重点;后面一部分,以查询数据为例,分析其他应用是如何访问ContentProvider暴露的数据。相信通过上面的分析,应该对ContentProvider的工作流程有比较好的认识了。同时,本篇文章到这里就结束啦~ ^_^相关文章推荐
- Android Activity的启动流程源码解析(8.0)
- android源码解析之(八)-->Zygote进程启动流程
- Android SystemServer启动流程源码解析
- Android Service的启动流程源码分析(8.0)
- 8.0源码解析:Activity启动流程
- Android源码基础解析之应用进程启动流程
- Android Launcher启动应用程序流程源码解析
- android源码解析之(十四)-->Activity启动流程
- Android源码基础解析之Zygote进程启动流程
- Android Zygote启动流程源码解析
- 【Android源码系列】ContentProvider启动源码解析
- Android源码解析之(八)-->Zygote进程启动流程
- Android源码解析之一Activity启动时界面绘制流程
- Android Activity启动流程源码解析
- Android Activity启动流程源码解析
- Android Activity启动流程源码全解析(1)
- android源码解析之(十)-->Launcher启动流程
- android源码解析之(十二)-->系统启动并解析Manifest的流程
- android源码解析之(十四)-->Activity启动流程
- Android源码基础解析之Launcher启动流程