前言
ContentProvider是一种内容共享型组件,它通过Binder向其他组件或应用提供数据。
当ContentProvider所在进程启动时,ContentProvider会同时启动并被发布到AMS中。需要注意的是,这时ContentProvider的onCreate先于Application的onCreate执行。
外界无法直接访问ContentProvider,只能通过AMS根据Uri获取对应ContentProvider的Binder接口IContentProvider,然后再通过IContentProvider来访问ContentProvider中的数据源。
访问ContentProvider需要通过ContentResolver,ContentResolver是一个抽象类,通过Context的getContentResolver方法获取。真正实现是ApplicationContentResolver(ContextImpl的内部类)。当ContentProvider所在的进程未启动时,第一次访问ContentResolver的方法时,就会启动该进程和ContentProvider。
通过增删改查四个方法的任何一个都可以触发ContentProvider的启动,这里选择query方法分析:
源码分析
ContentResolver#query
在ContentResolver的query方法中,需要先要获取IContentProvider对象1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public final Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal) {
IContentProvider unstableProvider = acquireUnstableProvider(uri);
//...
try {
//...
stableProvider = acquireProvider(uri);
//...
}
}
acquireUnstableProvider方法最终也是调用acquireProvider方法,所以接下来看ApplicationContentResolver的acquireProvider方法:
ApplicationContentResolver#acquireProvider
1 |
|
ApplicationContentResolver交给ActivityThread来处理,继续看ActivityThread的acquireProvider方法:
ActivityThread#acquireProvider
1 | public final IContentProvider acquireProvider( |
ActivityThread会先查找是否已经存在目标ContentProvider,如果存在就直接返回已存在的ContentProvider。否则通知AMS启动目标ContentProvider,最后通过installProvider方法修改引用数量。
在AMS中,首先启动ContentProvider所在的进程,然后再启动ContentProvider。新进程启动后,其入口方法为ActivityThread的main方法:
ActivityThread#main
1 | public static void main(String[] args) { |
在该方法中会创建ActivityThread实例,并调用其attach方法进行一系列初始化。
ActivityThread#attach
1 | private void attach(boolean system) { |
在attach中,会通过AMS的attachApplication方法将ApplicationThread对象传递给AMS
ActivityManagerService#attachApplication
1 |
|
继续调用attachApplicationLocked方法
ActivityManagerService#attachApplicationLocked
1 | private final boolean attachApplicationLocked(IApplicationThread thread, |
又调用了ApplicationThread的bindApplication方法
ActivityThread.ApplicationThread#bindApplication
1 | public final void bindApplication(String processName, ApplicationInfo appInfo, |
该方法发送一条消息给mH,mH对这条消息的处理是:1
2AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
继续看ActivityThread的handleBindApplication方法:
ActivityThread#handleBindApplication
1 | private void handleBindApplication(AppBindData data) { |
在该方法中,通过调用installContentProviders方法,来启动该进程的ContentProvider。
ActivityThread#installContentProviders
1 | private void installContentProviders( |
在该方法中,遍历provider集合,启动每个ContentProvider,最后把启动的ContentProvider存储在AMS的ProviderMap中。下面看一下启动ContentProvider的installProvider方法:
ActivityThread#installProvider
1 | private ContentProviderHolder installProvider(Context context, |
在该方法中,先创建完ContentProvider,并获得它的IContentProvider实例。然后,通过ContentProvider的attachInfo方法来调用它的onCreate方法。
至此,ContentProvider启动完成。
小结
ContentResolver调用增删改查的任意一个方法都可以触发ContentProvider的启动,要访问ContentProvider需先获得ContentProvider的IContentProvider对象,ContentResolver将这个任务交给ActivityThread。ActivityThread会先查找是否已经存在目标ContentProvider,如果存在就直接返回已存在的ContentProvider。否则通知AMS启动目标ContentProvider。
在AMS中,首先启动ContentProvider所在的进程,新进程启动后,其入口方法为ActivityThread的main方法,ActivityThread会将ApplicationThread对象传递给AMS,AMS之后又会通过该ApplicationThread对象通知ActivityThread启动该进程的ContentProvider,ActivityThread会创建每个ContentProvider并调用其onCreate方法,最后把启动的ContentProvider存储在AMS的ProviderMap中。
参考
- 《Android 开发艺术探索》