ContentProvider启动流程分析(二)

《ContentProvider启动流程分析(二)》

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

本片博客对应时序图上的step6—14上接第一篇博客:ContentProvider启动流程分析一! 下接第三篇ContentProvider启动流程分析三!

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

## 0x02 ContentProvider启动流程分析

step6: ActivityManagerProxy#getContentProvider()

代理类ActivityManagerProxy位于ActivityManagerNative.java文件中,其getContentProvider()成员函数的源码如下:

class ActivityManagerProxy implements IActivityManager {
    ....
    public ContentProviderHolder getContentProvider(IApplicationThread caller,
            String name, int userId, boolean stable) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeString(name);
        data.writeInt(userId);
        data.writeInt(stable ? 1 : 0);
        mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
        reply.readException();
        int res = reply.readInt();
        ContentProviderHolder cph = null;
        if (res != 0) {
            cph = ContentProviderHolder.CREATOR.createFromParcel(reply);
        }
        data.recycle();
        reply.recycle();
        return cph;
    }
}
  • getContentProvider()函数中,前面7行代码,将传进来的参数封装到一个Parcel对象中;
  • 然后再通过ActivityManagerProxy内部的一个Binder代理对象mRemote,向ActivityManagerService发送一个类型为GET_CONTENT_PROVIDER_TRANSACTION的进程间通信的请求;
  • 最后,再将ActivityManagerService返回的对象封装成一个ContentProviderHolder对象返回给调用者。

需要注意:以上step1—6是在应用程序AxxApp所在进程中执行的;接下来的step7—9在ActivityManagerService中执行,主要用来处理MainActivity组件发出的类型为GET_CONTENT_PROVIDER_TRANSACTION的进程间通信的请求!

step7: ActivityManagerService#getContentProvider()

ActivityManagerService类的成员函数getContentProvider()源码如下:

public final class ActivityManagerService extends ActivityManagerNative
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
    ....
    @Override
    public final ContentProviderHolder getContentProvider(
            IApplicationThread caller, String name, int userId, boolean stable) {
        ....
        return getContentProviderImpl(caller, name, null, stable, userId);
    }
    ....
}
  • 成员函数getContentProvider(),是用来处理类型为 GET_CONTENT_PROVIDER_TRANSACTION 的进程间通信的请求的;
  • 其内部直接调用getContentProviderImpl(),来进一步获得参数name对应的ContentProviderHolder对象。

step8: ActivityManagerService#getContentImpl()

getCOntentImpl()函数的作用是,获得参数name对应的ContentProviderHolder对象,这个对象用来描述一个ContentProvider组件的代理对象;getCOntentImpl()函数源码较长,我做了部分删减后如下:

《ContentProvider启动流程分析(二)》

  • 第10行,调用成员函数getRecordForAppLocked(),用来获取发出访问请求的应用程序所在进程的相关信息,在我们的场景中,也就是AxxApp应用程序所在进程的相关信息。这些信息,用一个ProcessRecord对象 r 来描述!
  • 在ActivityManagerService中,每一个已经启动的ContentProvider组件都使用一个ContentProviderRecord对象来描述。这些ContentProviderRecord对象保存在ActivityManagerService类的成员变量mProviderMap中!
  • mProviderMap实质上是一个HashMap,可通过两种方式进行查找,查找结果是一个ContentProviderRecord对象cpr:
    1. mProviderMap.getProviderByName()函数,通过参数name和参数userId进行查找;参数name指向了将要启动的ContentProvider组件的android:authorities属性值,也就是URI值对应的权限部分;参数userId则是调用方应用程序的Linux用户ID;
    2. mProviderMap.getProviderByClass()函数,通过ContentProvider组件的类名进行查找;

需要注意的是,mProvidersByName和mProvidersByClass的内部实现是哈希表,虽然都是保存的ContentProviderRecord对象,但是mProvidersByName变量是以ContentProvider组件的android:authorities属性值为关键字的,而mProvidersByClass变量是以ContentProvider组件的类名作为关键字的!

第15行,首先通过参数name和userId,mProviderMap.getProviderByName()检查是否存在对应的ContentProvider组件?接下来的逻辑很简单,分为两种情况:

  1. 第18行到27行代码,就是表示这个ContentProvider组件已经发布(启动)了,此时调用方(AxxApp)就可以直接通过ContentProviderRecord对象cpr来创建一个ContentProviderHolder对象;需要做两点说明:
    • ContentProviderRecord对象用来记录ContentProvider组件的发布(启动)信息,通过其对象cpr的provider成员变量来记录ContentProvider组件,它们是一对一的关系;
    • ContentProviderHolder是ContentProvider组件的代理对象,它持有一个ContentProvider对象;
  2. 第29到93行代码,就是表示这个ContentProvider组件还没有发布,需要进一步验证,第31行到35行通过mProviderMap.getProviderByClass()方式进行二次查找。如果还是没有找到?接下来就要启动这个ContentProvider组件了!
    • 第37到43行,先根据name参数和userId参数创建一个ContentProviderRecord对象;
    • 在启动cpr描述的ContentProvider组件之前,先判断这个COntentProvider组件的android:multiprocess属性值!如果为true,表示这个ContentProvider组件需要在访问它的应用程序进程中启动;为false表示需要创建第三方进程并在第三方进程中启动这个ContentProvider组件;也即第44到53行;
    • 第57行,关注常量mLaunchingProviders,ActivityManagewrService就是把正在启动的ContentProvider组件保存在mLaunchingProviders中;
    • 第59到65行,通过for循环判断目标ContentProvider组件的状态是否处于正在启动中。如果是,则等待它继续启动完成;
    • 如果不是,即66到83行,ActivityManagerService会调用成员函数startProcessLocked()新建一个应用程序进程,专门用来启动目标ContnrtProvider组件
    • 第85到89行,分别通过两种方式mProviderMap.putProviderByClass()和mProviderMap.putProviderByName()把启动的ContentProvider组件保存在mProviderMap变量中;
    • 最后,第93到99行,等待ContentProvider组件发布完成后,直接返回其对应的ContentProviderRecord对象即可;

step9: ActivityMangerService#startProcessLocked()

在step8中,目标ContentProvider组件是通过一个新建的第三方应用程序进程来启动!而这个第三方应用程序进程,就是通过ActivityMangerService的成员函数startProcessLocked()新建的。它主要通过调用Process的静态成员函数start()创建一个新的应用程序进程,并且把这个新建的应用程序进程的入口设置为ActivityThread类的静态成员函数main()!

以上三步都是在ActivityManagerService类中执行的!接下来我们分析新创建的应用程序进程,启动ContentProvider组件的过程,所以step10和step11是在第三方应用程序进程中完成的。

step10: ActivityThread#main()

ActivityThread.main()函数,会在新创建的应用程序进程中,创建一个ActivityThread对象和一个ApplicationThread对象,然后调用ActivityManagerProxy.attchApplication()函数,将ApplicationThread对象传递给ActivityManagerService!

step11: ActivityManagerProxy#attchApplication()

ActivityManagerProxy.attchApplication()函数,先向ActivityManagerService发出一个类型为ATTACH_APPLICATION_TRANSACTION的进程间通信请求,这个请求通过Binder机制处理!然后,就是把step10创建的ApplicationThread对象传递给ActivityManagerService!

接下来step12—14又回到ActivityManagerService中执行!主要用来处理新建应用程序进程发出的类型为ATTACH_APPLICATION_TRANSACTION的进程间通信请求。

step12: ActivityManagerService#attachApplication()

ActivityManagerService.attachApplication()函数,它调用了ApplicationManagerService类的成员函数attachApplicationLocked()来处理类型为ATTACH_APPLICATION_TRANSACTION的进程间通信请求,用来执行启动目标ContentProvider组件的操作!

接下来重点看看ApplicationManagerService类的成员函数attachApplicationLocked()!

step13: ApplicationManagerService#attachApplicationLocked()

ApplicationManagerService类的成员函数attachApplicationLocked()较长,删减后如下:

private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
    // Find the application record that is being attached...  either via
    // the pid if we are running in multiple processes, or just pull the
    // next app record if we are emulating process with anonymous threads
    ProcessRecord app;
    if (pid != MY_PID && pid >= 0) {
        synchronized (mPidsSelfLocked) {
            app = mPidsSelfLocked.get(pid);
        }
    } else {app = null;}

    try {
        AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);
        thread.asBinder().linkToDeath(adr, 0);
    }
    
    boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
    List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
    try {
        ProfilerInfo profilerInfo = profileFile == null ? null :
                                    new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop);
        thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                               profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                               app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                               isRestrictedBackupMode || !normalMode, app.persistent,
                               new Configuration(mConfiguration), app.compat,
                               getCommonServicesLocked(app.isolated),
                               mCoreSettingsObserver.getCoreSettingsLocked());
    }
    return true;
}
  • 注意到,参数pid指向前面所创建的应用程序进程PID,也即第三方应用进程PID,ProcessRecord对象app就是用来记录新建应用的进程信息的。
  • 在前面的step8中,ActivityManagerService以这个PID为关键字将一个ProcessRecord对象保存在了mPidsSelfLocked变量中,这里,首先通过参数pid取回ProcessRecord对象并保存在局部变量app中,然后调用generateApplicationProvidersLocked(app)函数需要在app所描述的第三方进程中启动的ContentProvider组件!

generateApplicationProvidersLocked()函数源码如下:

private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord       app) {
    List<ProviderInfo> providers = null;
    try {
        ParceledListSlice<ProviderInfo> slice = AppGlobals.getPackageManager().
            queryContentProviders(app.processName, app.uid,
                    STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
        providers = slice != null ? slice.getList() : null;
    }
    int userId = app.userId;
    if (providers != null) {
        int N = providers.size();
        app.pubProviders.ensureCapacity(N + app.pubProviders.size());
        for (int i=0; i<N; i++) {
            ProviderInfo cpi =
                (ProviderInfo)providers.get(i);
            boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
                    cpi.name, cpi.flags);
            if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_OWNER) {
                // This is a singleton provider, but a user besides the
                // default user is asking to initialize a process it runs
                // in...  well, no, it doesn't actually run in this process,
                // it runs in the process of the default user.  Get rid of it.
                providers.remove(i);
                N--;
                i--;
                continue;
            }

            ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
            ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
            if (cpr == null) {
                cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton);
                mProviderMap.putProviderByClass(comp, cpr);
            }
            app.pubProviders.put(cpi.name, cpr);
            if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
                // Don't add this if it is a platform component that is marked
                // to run in multiple processes, because this is actually
                // part of the framework so doesn't make sense to track as a
                // separate apk in the process.
                app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.versionCode, mProcessStats);
            }
            ensurePackageDexOpt(cpi.applicationInfo.packageName);
        }
    }
    return providers;
}
  • 先去PackageMangerService找目标ContentProvider组件,并保存到列表providers中;
  • 然后,for循环检查ActivityManagerService是否已经为这些ContentProvider组件创建过对应的ContnetProviderRecord对象来记录每一个ContentProvider组件;
  • 如果没有,则分别为它们创建一个ContentProviderRecord对象,并以它们的类名作为关键字保存在AMS的全局变量mProviderMap中!

从这里可以看出,一个应用程序进程在启动的时候,会将它所需要的所有ContentProvider组件全部启动起来!

最后,继续关注thread.bindApplication()函数,也即ActivityThread.bindApplication()函数!

step14: ActivityThreadProxy#bindApplication()

ActivityThreadProxy.bindApplication()函数源码如下:

public final void bindApplication(String packageName, ApplicationInfo info,
        List<ProviderInfo> providers, ComponentName testName, ProfilerInfo profilerInfo,
        Bundle testArgs, IInstrumentationWatcher testWatcher,
        IUiAutomationConnection uiAutomationConnection, int debugMode,
        boolean openGlTrace, boolean restrictedBackupMode, boolean persistent,
        Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
        Bundle coreSettings) throws RemoteException {
    Parcel data = Parcel.obtain();
    data.writeInterfaceToken(IApplicationThread.descriptor);
    data.writeString(packageName);
    info.writeToParcel(data, 0);
    data.writeTypedList(providers);
    if (testName == null) {
        data.writeInt(0);
    } else {
        data.writeInt(1);
        testName.writeToParcel(data, 0);
    }
    if (profilerInfo != null) {
        data.writeInt(1);
        profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    } else {
        data.writeInt(0);
    }
    data.writeBundle(testArgs);
    data.writeStrongInterface(testWatcher);
    data.writeStrongInterface(uiAutomationConnection);
    data.writeInt(debugMode);
    data.writeInt(openGlTrace ? 1 : 0);
    data.writeInt(restrictedBackupMode ? 1 : 0);
    data.writeInt(persistent ? 1 : 0);
    config.writeToParcel(data, 0);
    compatInfo.writeToParcel(data, 0);
    data.writeMap(services);
    data.writeBundle(coreSettings);
    mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null,IBinder.FLAG_ONEWAY);
    data.recycle();
}
  • 先创建一个Parcel对象data用来保存接收的参数;
  • 然后,mRemote是ActivityThreadProxy内部的一个Binder代理对象,通过mRemote向新建的应用程序进程(也即,第三方进程)发送一个类型为BIND_APPLICATION_TRANSACTION的进程间通信请求。

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

以上三步都是在ActivityManagerService中执行的,接下来的step15—24在新建的应用程序进程中执行,主要用来处理类型为BIND_APPLICATION_TRANSACTION的进程间通信请求!对应时序图上的step15—24

    原文作者:布鲁克林一棵树
    原文地址: http://www.cnblogs.com/wondertwo/p/5954655.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞