一、概述
ContentProvider作为Android四大组件之一,是Android应用对外开放的数据接口,只要符合它所定义的Uri格式的请求,均可以正常访问执行操作。其他的Android应用可以使用ContentResolver对象通过与ContentProvider同名的方法请求执行,被执行的就是ContentProvider中的同名的方法。
本文主要介绍ContentProvider的启动流程。
二、正文

类图1

类图2

时序图1

时序图2
1.A应用发送信息给AMS(ActivityManagerService,进程system_server)要访问B应用的XXXContentProvider
2.AMS检查B应用没有被启动过,则新开一个进程启动B应用
3.启动应用B后,AMS向B应用启动XXXContentProvider,并实行相应的onCreate,返回IActivityManager.ContentProviderHolder,其中实现IContentProvider接口的Transact
4.AMS 把IActivityManager.ContentProviderHolder对象返回给A应用,A应用改造成代理ContentProviderProxy(即IContentProvider),此时A应用就可以调用增删改查等接口到B应用了。
照着类图和时序图的步数来分析:
在A应用进程处理:Step1、2、3、4、5、6、18、19
在AMS system_server进程处理:Step7、10、11、12、17
在B应用进程处理:Step8、9、13、14、15、16、20
Step1:
ContentResolver contentResolver = Conext.getContentResolver();
返回是ApplicationContentResolver,定义在ContextImpl内部静态类。
Step2:
Uri uri = Uri.parse(“content://cn.umbrella.providers.contact/item”);
Cursor cursor = contentResolver.query(uri, new String[]{“id”,“name”,“phone”}, null, null, “id asc”);
在ApplicationContentResolver.acquireProvider()调用ActivityThread类的acquireProvider函数进一步执行获取Content Provider接口的操作。
Step3、4、5:
ActivityThread. acquireProvider:先本地查找,若有,则直接返回,没有则调用ActivityManagerNative.getDefault().getContentProvider(getApplicationThread(), auth, userId, stable)即ActivityManagerProxy到AMS的getContentProvider()。
Step6:
在AMS. getContentProvider()调用getContentProviderImpl进一步处理,
在AMS中:ProviderMap mProviderMap成员变量是保存系统中的ContentProvider信息,boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed先检查XXXContentProvider存在、宿主进程以及没有被杀的情况下,就直接返回。
若不存在,则会通过AppGlobals.getPackageManage().resolveContentProvider和getApplicationInfo来分别获取XXXProvider应用程序的相关信息,并保存cpi和cpr变量中。接下来会判断mLaunchingProviders(系统中所有正在加载的Content Provider都保到这里面)是不是正在被其它应用程序加载XXXContentProvider,如果B应用进程已开启,但对应的contentprovider未初始化过,则会走proc.thread.scheduleInstallProvider(cpi),到B应用的ActivityThread的handleInstallProvider、installContentProviders初始化contentProvider,略过Step7-13步直接Step14。
如果B应用进程未启动,则继续走Step7 调用startProcessLocked函数来启动新进程并加装XXXContentProvider,并且把这个正在加载的信息增加到mLaunchingProviders中去,同步等到XXXContentProvider初始化完,while循环cpr.provider判空,然后cpr.wait(),等到Step17步publishContentProviders会dst.notifyAll()过来。
Step7、8、9、10:
ActivityManagerService.startProcessLocked、Process.start、ActivityThread.main、ActivityThread.attach、ActivityManagerService.attachApplication 新进程的创建完回到AMS的过程,可参考startService源码从AMS进程到service的新进程启动过程分析
Step11:
AMS. attachApplicationLocked, 会对这个B应用进程记录块做一些初始化,并获得需要加装的ContentProvider列表,即包括XXXContentProvider,然后调用从参数传进来的IApplicationThread对象thread(即ApplicationThreadProxy)的bindApplication进入到B应用程序XXXContentProvider进程中的ApplicationThread对象的bindApplication函数中去。
Step12、13:
ApplicationThread(ActivityThread 变量). bindApplication通过H(Handler)到ActivityThread主进程中handleBindApplication处理,调用installContentProviders函数来在初始化XXXContent Providers信息,以及Application等初始化工作。
Step14、15:
ActivityThread.installContentProviders 先调用installContentProviders对XXXContentProvider的初始化attachInfo,并调用onCreate,并把ContextProvider的成员变量Transport(父类ContentProviderNative,是binder对象,并且实现了IContentProvider接口)然后调用ActivityManagerNative.getDefault().publishContentProviders(getApplicationThread(), results),到Step16.
Step16:
ActivityManagerProxy. publishContentProviders(IApplicationThread caller, List providers)通知AMS进程 ,B应用进程及XXXContentProvider都已初始化完毕,并把相应的IContentProvider给过去。
Step17 :
AMS. publishContentProviders:会把B应用进程的的ContentProvider保存起来,并移除mLaunchingProviders里面相应的值,然后通知dst.notifyAll();到Step6的getContentProviderImpl,接着返回给A应用进程的ContentProviderHolder对象。
Step18:
在AMS进程通过binder返回给A应用对象ContentProviderHolder,会把B应用进程的IContentProvider接口(即Transport)通过ContentProviderNative.asInterface(source.readStrongBinder())改造为ContentProviderProxy。
Step19、20:
A应用进程拿到B应用进程的Transport后, ContentResolver.query()中调用ContentProviderProxy.query,这里面会BulkCursorToCursorAdaptor,包含CursorWindow等匿名共享内存方式读取数据。
附上一些关键的源码:
ContextImpl.java:
class ContextImpl extends Context {
private final ApplicationContentResolver mContentResolver;
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
...
private static final class ApplicationContentResolver extends ContentResolver {
private final ActivityThread mMainThread;
private final UserHandle mUser;
...
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
...
}
}
ContentResolver.java:
public abstract class ContentResolver {
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) {
Preconditions.checkNotNull(uri, "uri");
IContentProvider unstableProvider = acquireUnstableProvider(uri);
...
try {
qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
}
...
}
}
ActivityThread.java:
public final class ActivityThread {
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;
}
...
IActivityManager.ContentProviderHolder holder = null;
try {
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
...
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
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);
...
IContentProvider provider = pr.mProvider;
IBinder jBinder = provider

本文详细分析了Android中ContentProvider的启动流程,从A应用如何通过ContentResolver访问B应用的ContentProvider,涉及到AMS的进程管理、ContentProvider的创建与初始化,以及跨进程通信的过程。
最低0.47元/天 解锁文章
2万+

被折叠的 条评论
为什么被折叠?



