Android ContentProvider启动流程源码解析(8.0)
来源:互联网 发布:日本原单淘宝店 编辑:程序博客网 时间:2024/06/10 23:05
一,写在前面
在文章 Android 如何自定义一个ContentProvider中,介绍了如何使用以及自定义一个ContentProvider,本篇不再介绍如何使用ContentProvider访问其他应用的数据。在阅读本篇文章前,建议先了解Activity的启动流程,可参考文章 Android Activity的启动流程源码解析(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方法源码:
@Overrideprotected 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的工作流程有比较好的认识了。同时,本篇文章到这里就结束啦~ ^_^
阅读全文
0 0
- Android ContentProvider启动流程源码解析(8.0)
- 【Android源码系列】ContentProvider启动源码解析
- Android Activity的启动流程源码解析(8.0)
- Android Zygote启动流程源码解析
- Android SystemServer启动流程源码解析
- Android Launcher启动应用程序流程源码解析
- Android Activity启动流程源码解析
- Android Activity启动流程源码解析
- Android Zygote启动流程源码解析
- Android SystemServer启动流程源码解析
- Android Launcher启动应用程序流程源码解析
- Android Activity启动流程源码解析
- Android ContentProvider启动流程完全解密
- Activity 启动流程源码解析
- Service 启动流程源码解析
- PackageManagerService启动流程源码解析
- springmvc启动流程源码解析
- PackageManagerService启动流程源码解析
- Drop Eggs
- 机器学习算法的python实现(1)---k近邻算法(kNN)
- recyclerview 通用gridlayoutmanger itemdecoration 间距。。可以含头部
- git命令行
- cookie与session的区别与联系
- Android ContentProvider启动流程源码解析(8.0)
- 通过php-fpm status判断pm.max_childern的值应该设置为多少
- Angular2学习笔记1
- MySQL数据备份和还原
- 用两个栈实现队列
- Java线程池机制源码分析
- 数据库连接池c3p0和dbcp
- Manacher算法——找字符串最长的回文子串
- hdu1305(字典树)