ContentProvider的工作过程
ContentProvider是一种内容共享组件,通过Binder向其他组件乃至其他应用提供数据。当ContentProvider所在的进程启动时,ContentProvider会同时启动并被发布到AMS中。
ContentProvider的onCreate要先于Application的onCreate而执行。
ContentProvider的启动过程
当一个应用启动时,入口方法为ActivityThread的main方法,main方法是一个静态方法,在main方法中会创建ActivityThread的实例并创建主线程的消息队列,然后在ActivityThread的attach方法中会远程调用AMS的attachApplication并将ApplicationThread对象提供给AMS。
ApplicationThread是一个Binder对象,它的Binder接口是IApplicationThread,它主要是ActivityThread和AMS之间的通信。
AMS的attachApplication会调用ApplicationThread的bindApplication方法,此过程是跨进程完成的。bindApplication的逻辑会经过ActivityThread中的mH Handler切换到ActivityThread中去执行,具体方法是handleBindApplication:ActivityThread会先创建Application对象,并加载ContentProvider,然后再调用Application的onCreate方法。
ContentProvider的使用
提供CRUD四个几口来操作ContentProvider中的数据源,insert、delete、update和query四个方法。
这四个方法都是通过Binder来调用的,外界无法直接访问ContentProvider,只能通过AMS根据uri来获取对应的ContentProvider的Binder接口IContentProvider,然后再通过IContentProvider来访问ContentProvider中的数据源。
android:multiprocess 默认为false,即ContentProvider为单实例,若为true时表名每个调用者的进程中都存在一个ContentProvider对象。实际开发中多实例使用少。
访问ContentProvider
访问ContentProvider需要通过ContentResolver,ContentResolver是一个抽象类,通过Context的getContentResolver方法获取的实际上是ApplicationContentResolver对象,ApplicationContentResolver对象,其继承了ContentResolver并实现了ContentResolver中的抽象方法。当ContentProvider所在的进程未启动时,第一次访问它就会触发ContentProvider的创建,当然这也伴随着ContentProvider所在进程的启动。4个方法都会导致ContentProvider的启动。
这里举例Query方法
ContentProvider的query方法中,首先会获取IContentProvider对象,不管是通过acquireUnstableProvider方法还是直接通过acquireProvider方法,它们本质是一样的,都是通过acquireProvider来获取ContentProvider。
这里直接调用了ActivityThread的acquireProvider方法
先查询有没有需要的ContentProvider存在,有则直接返回,ActivityThread通过mProviderMaplaunch来存储已经启动的ContentProvider对象。
如果ContentProvider没有启动,那就发送一个进程间请求给AMS让其启动目标ContentProvider,再通过installProvider方法来修改引用计数。
启动ContentProvider
ContentProvider被启动时会伴随着进程的启动,在AMS中,首先会启动ContentProvider所在的进程,然后再启动ContentProvider。
启动进程主要是通过Process的start方法来完成一个新进程的启动,新进程启动后其入口方法为ActivityThread的main方法。
ActivityThread的main方法是一个静态方法,它内部首先会创建ActivityThread的实例调用attach方法来进行一系列初始化,然后开始消息循环。
ActivityThread的attach方法会将ApplicationThread对象通过AMS的attachApplication方法跨进程传递给AMS,最终AMS会完成ContentProvider的创建过程。
AMS的attachApplication方法调用了attachApplicationLocked方法,而attachApplicationLocked中又调用了ApplicationThread的bindApplication,这里涉及了AMS到Application的跨进程通信。
ActivityThread的bindApplication会发送一个BIND_APPLICATION类型消息给mH,mH是一个Handler,它收到消息后会调用ActivityThread的handleBindApplication方法。
ActivityThread的handleBindApplication则完成了Application的创建以及ContentProvider的创建
创建ContentProvider
创建ContextImpl和Instrumentation
创建Application
启动当前进程ContentProvider并调用其onCreate方法
此处的installContentProvider完成了ContentProvider的启动工作,它遍历当前进程的ProviderInfo的列表,并一一调用installProvider方法来启动它们,接着将已经启动的ContentProvider发布到AMS中,AMS会把它们存储在ProviderMap中,这样一来外部调用者就可以直接从AMS中获取ContentProvider了。
在installProvider方法中有下面一段代码,其通过类加载器完成了ContentProvider对象的创建:
通过provider的attachInfo来调用ContentProvider 的onCreate方法
Application的创建
调用ContentProvider的方法
经历四个步骤后,ContentProvider已经成功启动了,其所在进程的Application也已经启动,意味着ContentProvider所在的进程已经完成了整个的启动过程,然后其他应用可以通过AMS来访问这个ContentProvider了。拿到了ContentProvider以后,可以通过它所提供的接口方法来访问它。
这里的ContentProvider并不是原始的ContentProvider,而是ContentProviderde Binder类型对象,IContentProvider,IContentProvider的具体实现ContentProviderNative和ContentProvider.Transport,其中ContentProvider.Transport继承了ContentProviderNative,所以IContentProvider的实现者实际上ContentProvider.Transport。
因此其他应用调用IContentProvider的query方法时最终会以进程间通信的方式调用到ContentProvider.Transport的query方法。
ContentProvider.Transport的query方法调用了ContentProvider的query方法,query方法的执行结果再通过Binder返回给调用者。