• 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()函数源码较长,我做了部分删减后如下:

    AMS#getContentProviderImpl()

    • 第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

  • 相关阅读:
    第四次作业
    第三次作业
    java第三次作业
    java第二次作业
    java作业
    第一次作业
    第十一次作业
    第十次作业
    第九次作业
    第八次作业
  • 原文地址:https://www.cnblogs.com/wondertwo/p/5954655.html
Copyright © 2020-2023  润新知