ContentProvider启动流程分析(一)

2016-10-12  本文已影响1264人  一只酸奶牛哇

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;
    }
}

时序图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);
    }
}

时序图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;
        }
    }
}

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

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


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

上一篇下一篇

猜你喜欢

热点阅读