【学习】Android四大组件启动流程(4)——ContentProvider的工作过程

803 阅读4分钟

ContentProvider的工作过程

ContentProvider启动的大致流程

image.png
当ContentProvider所在的进程启动时,ContentProvider会同时启动并被发布到AMS,这个时候ContentProvider的onCreate要先于Application的onCreate而执行

当一个应用启动时,入口方法为ActivityThread的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。在handleBindApplication方法中,ActivityThread会创建Application对象并加载ContentProvider。要注意ActivityThread会先加载ContentProvider然后再调用Application的onCreate方法
image.png
外界不能直接访问ContentProvider,它只能通过AMS根据Uri来获取对应的ContentProvider的Binder接口IContentProvider,然后再通过IContentProvider来访问ContentProvider的数据源。

一般来说ContentProvider都应该是单实例的,但它到底是不是单实例有android:multiprocess属性来决定的,当为false时是单实例,也是默认值。为true时,在每个调用者的进程中都存在一个ContentProvider对象。在实际的开发中并未发现多实例的使用场景,官方文档解释说可以避免进程间通信的开销,但仍缺少使用价值。

访问ContentProvider需要通过ContentResolver,ContentResolver是一个抽象类,通过Context的getContentResolver获取的实际上是ApplicationContentResolver对象,ApplicationContentResolver类继承了ContentResolver并实现了其中的抽象方法。当ContentProvider所在的进程未启动时,第一次访问它时就会触发ContentProvider的创建,当然这也伴随着ContentProvider所在进程的启动。增删改查任意一个都可以触发ContentProvider的启动过程。

这里选择query方法
首先会获取IContentProvider对象,不管是通过acquireUnstableProvider方法还是直接通过acquireProvider方法,它们本质都一样,最终都是通过acquireUnstableProvider方法来获取ContentProvider

image.png

它直接调用了ActivityThread的acquireProvider方法

image.png
首先会从ActivityThread中查找师傅已经存在目标ContentProvider,如果存在就直接返回。ActivityThread中通过mProviderMap来存储已经启动的ContentProvider对象

image.png
如果目前ContentProvider没有启动,那么就发送一个进程间请求给AMS让其启动目标ContentProvider,最后再通过installProvider方法来修改引用计数。

在AMS中,首先会启动ContentProvider所在的进程,然后再启动ContentProvider。启动进程是由AMS的startProcessLocked方法来完成的,其内部主要是通过Process的start方法来完成一个新进程的启动,新进程启动后其入口方法是ActivityThread的main方法 image.png
它是一个静态方法,在它内部首先会创建ActivityThread的实例并调用attach方法来进行一系列初始化,接着开始进行消息循环。attach方法会将ApplicationThread对象通过AMS的attachApplication方法跨进程传递给AMS,最终AMS会完成ContentProvider的创建过程
image.png
AMS的attachApplication方法调用了addtachApplicationLocked方法,attachApplicationLocked又调用了ApplicationThread的bindApplication,这也是进程间调用
image.png
它会发送一个BIND_APPLICATION类型的消息给mH,mH是一个Handler,它收到消息后会调用ActivityThread的handleBindApplication方法

bindApplication方法发送消息过程

image.png ActivityThread的handleBindApplication则完成了Application的创建以及ContentProvider的创建,分四大步骤

1.创建ContextImpl和Instrumentation

在handleBindApplication方法中会调用initInstrumentation方法
image.png

2.创建Application对象

image.png

3.启动当前进程的ContentProvider并调用其onCreate方法

image.png
installContentProviders完成了ContentProvider的启动工作,它的实现如下所示。 image.png
首先会遍历当前进程的ProviderInfo列表并一一调用installProvider方法来启动他们,接着将已经启动的ContentProvider发布到AMS中,AMS会把他们存储在ProviderMap中,这样一来外部调用者就可以直接从AMS中获取ContentProvider了。

在installProvide方法中通过类加载器完成了ContentProvider对象的创建,还会通过ContentProvider的attachInfo方法来调用它的onCreate方法

image.png image.png
到此为止ContentProvider已经被创建并且onCreate已经被调用,意味着ContentProvider已经被创建完成了

4.调用Application的onCreate方法

image.png

经过上面四个步骤,ContentProvider已经成功启动,并且其所在进程的Application也已经启动,这意味着ContentProvider所在的进程已经完成了整个的启动过程,然后其他应用就可以通过AMS来访问这个ContentProvider了。拿到了ContentProvider以后,就可以通过它所提供的接口方法来访问它了。需要注意的是,这里的ContentProvider是ContentProvider的Binder类型的对象IContentProvider,IContentProvider的具体实现是ContentProviderNative和ContentProvider.Transport,后者继承了ContentProviderNative。

仍然选择query方法,首先其他应用会通过AMS获取到ContentProvider的Binder对象IContentProvider,而IContentProvider的实现者实际上是ContentProvider.Transport。因此其他应用调用IContentProvider的query方法时最终会以进程间通信的方式调用到ContentProvider.Transport的query方法。
image.png ContentProvider.Transport的query方法调用了ContentProvider的query方法,它的执行结果再通过Binder返回给调用者。