ContentProvider启动流程分析(一)

0x01 扯东扯西的前言&概述


本片博客对应时序图上的step1—5下接第二篇ContentProvider启动流程分析二!

同时此系列博客同步在简书发布:ContentProvider启动流程分析系列!详情戳这里即可访问~

作为安卓设计的四大组件之一,是跨进程共享数据的一把利器,所谓跨进程共享数据,通俗理解就是,应用程序A可以访问操作应用程序B共享出来的数据,这些共享出来的数据一般都有其对应的URI(统一资源标识符),那么就涉及到两个过程:

  1. 提供数据内容的过程:
    • A应用(比如系统联系人,日历)如果希望共享自己的数据内容(比如联系人列表,日历信息),就需要通过子类重写ContentProvider六个方法来实现,ContentProvider的六个方法的共性特征是,都接受一个URI参数,通过解析URI参数匹配相应的数据内容并将其返回;
    • 当一个跨进程访问数据的请求(包含RUI参数)发出后,系统首先会校验URI权限(authority),权限校验通过后,把这个访问请求传递到对应进程(具体来说是一个应用程序)的ContentProvider组件,ContetnProvider接收到URI参数后,会解析URI路径(path),然后根据路径解析结果,提供相应的数据内容并将其返回;
    • 此过程在A应用程序内部实现,当A应用程序封装好了ContentProvider实现类,需要在Mainfest清单文件中进行注册,至此,A应用程序所在的进程已经提供了访问自己所在进程数据的接口,B应用程序只需要根据数据内容的URI,发出访问请求即可;
  2. 发出访问数据请求的过程:
    • B应用程序如果希望跨进程访问A应用程序共享出来的数据,需要调用Context#getContentResolver()#query()|update()|insert()|delete(),无非就是对数据内容进行增删改查操作,涉及到一个类ContentResolver,具体调用的是ContentResolver的增删改查方法;与SQLite数据库的增删改查不同,ContentResolver的增删改查方法需要接受一个URI参数,这个URI参数就是希望访问的数据内容的URI;
    • 此过程在B应用程序内部实现,通过在B进程访问A进程的私有数据,完成跨进程共享数据的过程!

模拟一个跨进程请求数据的场景:A应用程序(AxxApp)在MainActivity中向B应用程序(BxxApp,也即系统自带的联系人应用)的联系人数据发起跨进程的访问请求。B应用程序中,SubContentProvider类继承ContentProvider组件类,并重写了ContentProvider的六个成员函数。

根据以上场景,当A应用程序发出访问请求,请求携带系统联系人数据URI,系统联系人数据对应的URI值是 ContactsContract.CommonDataKinds.Phone.CONTENT_URI,当访问请求发出后,系统会根据对应的URI,启动B应用程序中ContentProvider组件SubContentProvider,并把联系人数据返回给A应用程序。那么B程序的SubContentProvider组件是经过哪些调用,一步一步被启动的呢?请看时序图如下:

ContentProvider启动时序图

0x02ContentProvider启动流程分析


再来结合源码分步梳理一遍详细经过,对应时序图的step1-->step5,过程如下:

时序图step1 --> Context#getContentResolver()

在A程序进程的MainActivity中调用getContentResolver函数,根据MainActivity的多重继承关系,MainActivity继承了Activity,而Activity又继承了ContextWrapper,所以我们可以发现,因为当前Activity持有Context的引用,所以实际上调用的是ContextWrapper.getContentProvider()函数来获得ContentResolver对象,记作resolver变量。

ContextWrapper类的成员函数getContentResolver()源码如下:

public class ContextWrapper extends Context {
    Context mBase;
	....
    @Override
    public ContentResolver getContentResolver() {
        return mBase.getContentResolver();
    }
	....
}

可以看到ContextWrapper类的成员变量mBase指向了一个ContextImpl对象,它是在MainActivity组件启动时创建的,因此mBase.getContentResolver()实际上的调用是ContextImpl.getContentResolver()函数来获得一个ContentResolver对象resolver的。

ContextImpl类的成员函数getContentResolver()源码如下:

class ReceiverRestrictedContext extends ContextWrapper {
	....
	private final ApplicationContentResolver mContentResolver;
	....
	/*构造函数*/
    private ContextImpl(ContextImpl container, ActivityThread mainThread,
            LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
            Display display, Configuration overrideConfiguration, int createDisplayWithId) {
		....
		/*创建ApplicationContentResolver对象*/
        mContentResolver = new ApplicationContentResolver(this, mainThread, user);
    }
	
	/*返回ContentResolver对象*/
    @Override
    public ContentResolver getContentResolver() {
        return mContentResolver;
    }
}
  • ContextImpl类的成员变量mContentResolver,是一个ApplicationContentResolver对象,在构造方法中被创建后,直接在getContentResolver()函数中简单的被返回给调用者。
  • 也即,MainActivity中getContentResolver()函数,最终获得的是一个ApplicationContentResolver对象;
  • 接着就调用这个ApplicationContentResolver对象的acquireProvider()函数获取BxxApp应用程序的SubContentProvider组件的一个代理对象;
  • 也就是获得与ContactsContract.CommonDataKinds.Phone.CONTENT_URI联系人数据URI对应的一个ContentProvider组件对象。

时序图step2,3 --> ContentResolver#acquireProvider()

ApplicationContentResolver是ContentResolver的实现类,它重写了父类的acquireProvider()函数,所以实际上调用的是ContentResolver子类ApplicationContentResolver的成员函数acquireProvider(),ApplicationContentResolver#acquireProvider()源码如下:

private static final class ApplicationContentResolver extends ContentResolver {
	private final ActivityThread mMainThread;
	....

	public ApplicationContentResolver(
            Context context, ActivityThread mainThread, UserHandle user) {
        super(context);
        mMainThread = Preconditions.checkNotNull(mainThread);
		....
    }

    @Override
    protected IContentProvider acquireProvider(Context context, String auth) {
        return mMainThread.acquireProvider(context,
                ContentProvider.getAuthorityWithoutUserId(auth),
                resolveUserIdFromAuthority(auth), true);
    }
}
  • 可以看出ApplicationContentResolver类的成员变量mMainThread指向了一个ActivityThread对象,它是在构造函数中初始化的。
  • 在函数acquireProvider内部,其实调用的是ActivityThread类的成员函数acquireProvider(),这个函数会返回一个ContentProvider组件的代理对象,而这个代理对象代理的,正是联系人数据URI对应的COntentProvider组件!

时序图step4—5 --> ActivityThread#acquireProvider()/acquireExistingProvider()

接下来看ActivityThread类的acquireProvider()函数的源码如下:

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);
        } ....
        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);
            if (pr == null) {
                return null;
            }

            IContentProvider provider = pr.mProvider;
            IBinder jBinder = provider.asBinder();
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
            if (prc != null) {
                incProviderRefLocked(prc, stable);
            }
            return provider;
        }
	}
}
  • ActivityThread类的acquireProvider()函数中,首先通过acquireExistingProvider()函数得到一个ContentProvider组件的代理对象IContentProvider对象provider;

  • acquireExistingProvider()函数持有一个全局的mProviderMap变量,表示一个HashMap哈希表,用来保存在当前应用程序进程中访问过的ContentProvider组件的代理ProviderClientRecord对象;

  • 此函数的逻辑是,从mProviderMap表中获取与权限参数(auth)以及用户ID参数(userId)对应的ProviderClientRecord代理对象,并将其返回!

  • 再回到acquireProvider()函数中,得到provider对象后进行判空,如果provider非空则直接将其返回给调用者了;

  • 如果provider为空,接下来会先获得ActivityManagerService类的一个代理对象,接着调用ActivityManagerService代理对象的成员函数getContentProvider()来请求与权限参数(auth)以及用户ID参数(userId)对应的ContentProvider组件的代理对象;

  • 并通过ActivityManagerService将这个代理对象返回。ActivityManagerService返回的ContentProvider组件的代理对象,使用一个ContentProviderHolder对象来进行描述。

  • 接下来,会调用ActivityThread类的成员函数installProvider(),将这个ContentProviderHolder对象封装成一个ContentClientRecord对象,并把这个ContentClientRecord对象存入全局变量mProviderMap中。

  • 这样如果后面再次接收到,对相同URI对应的ContentProvider的访问请求,对于每个ContentProvider组件来说了,只需要被ActivityThread请求一次即可,当再次收到相同的请求只需要从全局变量mProviderMap中将其取出来返回给调用者即可!

然后按照函数执行的先后顺序,分为两个片段,先分析ActivityManagerService代理对象(记作ActivityManagerProxy)的成员函数getContentProvider()的具体实现(对应时序图step6—19),然后再分析ActivityThread类的成员函数installProvider()的具体实现(对应时序图step20)。

0x03 参考文献与简单的结语


未完,转接下一篇:ContentProvider启动流程分析二(对应时序图step6—14)!

posted @ 2016-10-12 22:22  布鲁克林一棵树  阅读(572)  评论(0编辑  收藏  举报