• ActivityManagerService的启动过程


    AMS对象随系统进程启动而构建,随着系统进程退出而消亡,可以说,AMS与系统进程共存亡。

    先上一张总的启动时序图:

    上图分为三个步骤:

    1. 初始化系统进程的运行环境;
    2. 初始化AMS对象;
    3. AMS对象启动的配套工作。

    1.初始化系统进程的运行环境

    SystemServer是我们理解Android系统进程的入口,它的初始化是从Native层开始的:Zygote从Native层调用SystemServer的main()函数,便开始了SystemServer的初始化。初始化很重要的一个步骤就是创建系统进程的运行环境,即创建一个SystemContext,调用关系如下所示:

    SystemServer.main()      // Zygote会从Native层调用该方法,进入SystemServer的执行代码
    └── SystemServer.run()  // SystemServer一旦运行起来,就一直循环处理消息队列中的消息
        └── SystemServer.createSystemContext()  // 创建SystemContext

    SystemContext到底是什么呢?说到底,它还是一个Context类型的对象,需要借助于ActivityThread才能获取到:

    // SystemServer.createSystemContext()
    private void createSystemContext() {
        ActivityThread activityThread = ActivityThread.systemMain();
        mSystemContext = activityThread.getSystemContext();
        mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
    }

    ActivityThread.systemMain()函数用于创建系统进程的主线程,方法主体很简单,创建了一个新的ActivityThread对象,并调用了attach()方法,输入参数是true,表示需要将其绑定到系统进程。

    应用进程创建ActivityThread对象是通过ActivityThread.main()函数完成的。由于系统进程的特殊性,专辟了一个systemMain()函数给系统进程。

    public static ActivityThread systemMain() {
        ... // 省略硬件渲染相关配置的代码
        ActivityThread thread = new ActivityThread();
        thread.attach(true);
        return thread;
    }

    接下来,我们来着重分析一下ActivityThread.attach()函数:

    private void attach(boolean system) {
        sCurrentActivityThread = this;
        mSystemThread = system;
        if (!system) {
            ... // 绑定应用进程,本例仅讨论绑定系统进程,故省略之
        } else {
            // 设置进程名为system_process
            android.ddm.DdmHandleAppName.setAppName("system_process", UserHandle.myUserId());
            try {
                mInstrumentation = new Instrumentation();
                ContextImpl context = ContextImpl.createAppContext(
                    this, getSystemContext().mPackageInfo);
                mInitialApplication = context.mPackageInfo.makeApplication(true, null);
                mInitialApplication.onCreate();
            } catch (Exception e) { ... // 省略抛出异常的代码 }
        }
    
        // 设置与输出日志相关的Dropbox
        DropBox.setReporter(new DropBoxReporter());
        ViewRootImpl.addConfigCallback( ... // );
    }

    在上面的函数实现中,会创建几个很重要的对象:

    • mInstrumentation: 工具类,主要用于测试,与AndroidManifest.xml中<instrumentation>对应;

    • context: Application的运行环境,创建它的目的是为了创建下面的Application对象。创建这个context需要传入一个LoadedApk类型的对象,通过ActivityThread.getSystemContext()函数便可获取:

    public ContextImpl getSystemContext() {
        synchronized (this) {
            if (mSystemContext == null) {
                mSystemContext = ContextImpl.createSystemContext(this);
            }
            return mSystemContext;
        }
    }

    这里是第一次调用getSystemContext()函数,所以mSystemContext为null,进而会通过ContextImpl.createSystemContext()函数创建一个新的对象:

    // ContextImpl.createSystemContext()
    static ContextImpl createSystemContext(ActivityThread mainThread) {
        LoadedApk packageInfo = new LoadedApk(mainThread);
        ContextImpl context = new ContextImpl(null, mainThread,
                packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY);
        context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
                context.mResourcesManager.getDisplayMetricsLocked());
        return context;
    }

    上述函数首先会创建一个LoadedApk的对象,LoadedApk表示一个装载到内存的Apk,对于SystemContext,这个Apk就是framework-res.apk; 然后,创建一个ContextImpl的对象,进一步初始化资源相关的配置; 最终,返回的context就是SystemContext。

    • mInitialApplication: Application对象,由于是通过framework-res.apk这个APK的Context创建而来,所以这个Application对象就对应到了framework-res.apk,这是系统中第一个运行的APK,所以叫做InitialApplication,它是运行在系统进程中。

    兜兜转转弄了一圈,终于把SystemContext给创建好了,系统进程好好运行就好了,为什么要大费周张的搞一个运行环境Context出来呢? 操作系统运行基本单位是进程,我们写的任何Android代码最终都要落到一个实际的进程中去执行。然而,Android有意弱化了进程的概念,我们在写Android应用程序的时候,基础的概念都是运行环境(Context),而不是去考虑进程中有什么可以使用。 Android对待系统进程,也像对待普通的应用进程一样,都需要构建一个运行环境,就是Context。在构建AMS对象时,会将SystemContext传入,通过这个特殊的Context,AMS就能获取运行时所需要各种信息。

    我们通过一张类图来总结一下,与系统进程运行环境初始化相关的各个类之间的关系:

     

      总结初始化环境的三个步骤:

    1. Android要为应用进程创造一个运行环境,同样也需要为系统进程创造一个运行环境,在系统进程启动伊始,这个运行环境就需要创建完毕,这个环境就是我们所说的SystemContext;

    2. SystemServer会创建一个ActivityThread对象,代表系统进程的主线程,系统进程的入口为SysMain(),在ActivityThread对象构建的过程中,又会创建Instrumentation对象和framework-res.apk的LoadedApk对象,再通过LoadedApk创建Application对象;

    3. 在ActivityThread对象构建完毕后,SystemServer便可获取到系统进程的运行环境了,即SystemContext,这是ActivityThread通过ContextImpl创建而成的。

     

    2.初始化ActivityManagerService对象

    在Android起机的过程中,系统服务是相互影响的,所以有启动顺序的先后之分,譬如BatteryService,WindowManagerService就需要在ActivityManagerService创建之后才能创建。还有一些系统事件,譬如BOOT_COMPLETED广播,需要在系统完全启动之后才能发出。 Android为系统服务封装了SystemService类,设计了系统服务的生命周期:

    public abstract class SystemService {
        // 阶段 1: 等待显示设备准备完毕
        // 这个阶段,Installer, AMS, PowerManagerService, DisplayManagerService已经构建完毕
        public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100;
        // 阶段 2: 锁屏服务已经准备完毕
        // 这个阶段,大部分系统服务已经构建完毕。其他服务可以获取锁屏相关的数据了,譬如锁屏密码等
        public static final int PHASE_LOCK_SETTINGS_READY = 480;
        // 阶段 3: 系统服务已经准备完毕
        // 这个阶段,大部分系统服务已经构建完毕,PackageManagerService,PowerManagerService已经能够提供服务
        public static final int PHASE_SYSTEM_SERVICES_READY = 500;
         // 阶段 4: ActivityManagerService已经能够提供服务了
         // 这个阶段,获取ContentProvider、发送广播等AMS相关的功能已经可以用了
        public static final int PHASE_ACTIVITY_MANAGER_READY = 550;
         // 阶段 5: 已经可以启动应用程序了
         // 这个阶段,用户界面已经启动,数据连接、音频等服务都已可用
        public static final int PHASE_THIRD_PARTY_APPS_CAN_START = 600;
        // 阶段 6: 系统起机完成
        public static final int PHASE_BOOT_COMPLETED = 1000;
    
        public abstract void onStart();
        public void onBootPhase(int phase) {}
    
        // ... 省略其他函数
    }

    部分系统服务的初始化依赖于当前系统已经起机到什么阶段,当系统服务启动时,onStart()函数会被调用; 系统启动到一定的阶段,onBootPhase()函数会被调用。所有系统服务的创建和生命周期的管理都是由SystemServiceManager这个类完成的。

    本文中要分析的AMS,它并没有直接继承SystemService,而是通过内部类Lifecycle来继承实现的, AMS.Lifecycle非常简单,就是调用了AMS的构造函数和start()函数,是一个间接初始化AMS的过程。

    public static final class Lifecycle extends SystemService {
        private final ActivityManagerService mService;
    
        // 构造函数会被SystemServiceManager反射调用
        public Lifecycle(Context context) {
            super(context);
            mService = new ActivityManagerService(context);
        }
    
        // 在Lifecycle对象构造完成后,这个函数会被回调
        @Override
        public void onStart() {
            mService.start();
        }
    
        public ActivityManagerService getService() {
            return mService;
        }
    }

    我们通过类图来总结一下SystemServiceManager, SystemService以及AMS三者之间的关系:

    1. SystemService是系统服务的抽象类,封装了onStart()和onBootPhase()等生命周期函数供SystemServiceManager回掉;

    2. AMS并不是直接继承SystemService,而是通过内部类Lifecycle间接完成了系统服务启动的生命周期;

    3. SystemServiceManager管理了多个SystemService。

    SystemServer在完成一些简单的初始化后,就需要启动系统服务了,最重要的一系列服务是在SystemServer.startBootstrapServices() 这个函数中启动的,BootstrapServices的命名也很贴切,表示要启动一些”引导服务”,这些服务直接影响到其他服务的启动。

    1. 启动AMS之前,需要先连接installd进程。在系统进程(system_process)中,对应的服务就Installer, 通过Installer这个服务,就能完成一些重要目录的创建,譬如/data/user。

    2. 启动AMS。由此可见AMS的重要性,Android第二个启动的系统服务就是AMS。 实际上,这里通过SystemServcieManager来启动的是AMS.Lifecycle,AMS的真正初始化工作是由AMS.Lifecyle间接完成的。

    3. 启动PowerManagerService,这也是非常重要的一个系统服务。AMS也需要使用PowerManagerService的服务, 譬如,在启动Activity时,要避免系统进入休眠状态,就需要获取WakeLock。

    4. 这一步启动Lights、 DisplayManager、 PackageManager这些系统服务。

    5. 调用AMS.setSystemProcess()将当前进程设置为系统进程。为什么在SystemServer中需要调用AMS的方法来设置当前进程的信息呢? 因为AMS的职责之一就是维护所有进程的状态,不管是应用进程还是系统进程,都是AMS的管辖范围。

       上面第2条AMS对象构建时,要初始化的内容非常多,大致可以分成两类:

    • 监测统计: Watchdog,CPU、内存、电量的使用统计
    • 组件管理: broadcastQueues, services, providers, statckSupervisor,recentTasks, Android四大组件的相关信息都由AMS统一维护

       在第5步中, 通过AMS对象调用了setSystemProcess()函数,目的是为了将当前进程(system_process)设置为系统进程:

    1. 通过ServiceManager向系统注册一些重要的服务。诸如meminfo、gfxinfo、dbinfo等信息都是由系统进程维护的, 可以通过adb shell dumpsys命令输出。

    2. ApplicationInfo包含了一个应用程序的信息,这些信息从AndroidManifest.xml的<application>标签中解析出来,譬如进程名、版本号、使用的主题等,那么,通过android这个包名,获取的ApplicationInfo自然就是Android系统这个应用程序的信息。

      每一个普通的应用程序都会有一个AndroidManifest.xml文件,这个应用程序运行的环境,就是我们所说的”应用进程”; 有一个特殊的应用程序,具备更多的特权,这个应用程序的运行环境就是”系统进程”。 android就是这个特殊应用程序的包名,其所对应的<application>标签定义在frameworks/base/core/res/AndroidManifest.xml中,最终会编译在framework-res.apk中。

      不论是应用进程还是系统进程,都有主线程,ActivityThread就是Android定义的主线程类。 AMS中的mSystemThread对象就是ActivityThread的实例,表示这是系统进程的主线程。 通过mSystemThread.installSystemApplicationInfo()这个函数调用,ApplicationInfo和ClassLoader就被设置到了 LoadedApk中,ApplicationInfo与LoadedApk的关系我们后文再描述。

    3. ProcessRecord这个类用于描述一个运行的进程,AMS管理着所有ProcessRecord的状态,包括系统进程的ProcessRecord。 系统进程的ProcessRecord几个重要的属性值:

      • persistent=true: 系统进程的ProcessRecord是常驻内存的
      • maxAdj=SYSTEM_ADJ(-16): 在内存不足时,这个值越小,存活的几率就越大。SYSTEM_ADJ已经是倒数第二小了,可见系统进程在内存不足时被杀的可能性极小。
      • active: 在上文中,我们介绍过ProcessRecord有Active和InActive两种状态,所谓”激活”,就是将应用程序的信息(IApplicationThread)绑定到进程,这样就能够通过ProcessRecord间接完成对进程的调度。

      AMS通过mPidSelfLocked这个映射表来记录所有的ProcessRecord,(键 => 值)关系是(PID => ProcessRecord)。 在创建一个ProcessRecord之后,就需要集中对进程的信息进行调整了,AMS中管理进程的函数就两类:updateLruProcessLocked()用于更新最近使用进程列表,updateOomAdjLocked()用户更新进程的OOM ADJ。

    3.AMS启动的配套工作

    在引导服务(BootstrapServices)启动完毕后,SystemServer就开始启动核心服务(CoreServices),包括电池服务(BatteryService),用户行为统计服务(UsageStatsService)等; 最后,就是启动其他服务了,非常之多,不再此列举。部分服务在启动时,仍需要与AMS关联,譬如: AMS需要与WindowManagerService关联。AMS在这个过程之中有两个关键的步骤:

    1. AMS.installSystemProviders(): 表示要装载SettingsProvider, 很多系统服务都需要从这个数据库中读取配置信息;
    2. AMS.systemReady(): 表示当前SystemServer已经启动完毕, AMS仍需要做一些准备就绪工作。

    3.1 对于installSystemProviders的分析

    关键函数 generateApplicationProvidersLocked()

    该函数基于AndroidManifest.xml文件的定义, 生成一个应用程序的Provider信息, 以方便AMS对Provider进行管理,

    AMS对Provider的管理方式:

    • AMS中使用ContentProviderRecord来管理一个ContentProvider。ProviderInfo, ApplicationInfo, 运行ContentProvider的ProcessRecord等信息都保存在ContentProviderRecord中。

    • AMS维护了一个ProviderMap, 支持从Authority或CompnentName到ContentProviderRecord的映射; AMS也维护了各种各样的ProcessRecord, 譬如: 前台进程, 内存常驻进程, 最近使用的进程等。 当一个ContentProvider绑定到具体的进程时, 就会添加到ProcessRecord中维护的mPubProviders的映射表中。所以, ProcessRecord.mPubProviders就表示进程所拥有的ContentProvider。

          现在,我们再来看这块函数的逻辑:

    • 首先, 需要确保ProcessRecord能够容纳一定数量的Provider, 前面通过PackageManager找到的ProviderInfo可能会关联到ProcessRecord中,所以, 在mPubProvider上已有容量的基础上, 再扩容的大小为找到的ProviderInfo的数量。

    • 然后, 对找到的ProviderInfo列表进行遍历, 如有需要, 则新建一个ContentProviderRecord对象, 将其添加到mProviderMap中以方便AMS管理;同时, 也需要将其添加到PrcoessRecord.mPubProviders中。

    • 最后, 由于可能新增其他APK中的ProviderInfo, 所以需要确保对APK进行dex优化。

    关键函数: ActivityThread.installSystemProviders()

    将一个ProviderInfo绑定到ProcessRecord后, AMS中就有了Provider的信息了, 但这时Provider还不能工作, 因为真正的ContentProvider还未创建, 该函数的就是将上面找到的ProviderInfo装载到系统进程之中

    所有ContentProvider的创建都需要经过installContentProvider()函数, 它接收两个参数, 一个是进程的运行环境Context, 一个是 ProviderInfo列表; 对系统进程而言, 运行环境就是mInitialApplication。该函数中使用了ContentProviderHodler这个类,它实现了Parcelable接口, 通常表示这类数据结构需要跨进程传递, 应用进程中生成的ContentProvider需要向系统进程注册后才能使用, 所以, 需要将ContentProvider的信息从应用进程传递到系统进程, 这就用到了ContentProviderHolder进行数据封装。

    基于ProviderInfo生成的ContentProviderHolder的函数实现是installProvider():

    3.2对于systemReady的分析

    1. mSystemReady表示系统进程是否准备完毕, 由于systemReady()函数会被多次调用到,而且多线程并行,所以一旦mSystemReady为true,表示不再需要执行下面的逻辑了,直接回调函数入参goingCallback,这个回调函数我们放到后面分析;

    2. 进入这一步,表示mSystemReady为false,第一次进入systemReady()函数时,就是这种场景。这里会恢复最近任务列表;

    3. 涉及到PRE_BOOT_COMPLETED广播的处理。这个广播在BOOT_COMPLETED广播之前发送,而且只发给系统应用。系统应用收到该广播后,也应该标注已经处理过该广播,下次不用再派发过来。设计PRE_BOOT_COMPLETED广播的目的,是为了应对系统升级的场景:当从旧的版本升级时,系统应用可能有一些清除数据的需要,系统升级后的第一次起机时,就会向接收者派发这个广播。

    PRE_BOOT_COMPLETED的派发实现在deliverPreBootCompleted()函数中,本文不展开分析。需要知道这里有两个控制变量:

    • mDidUpdate: 默认为false; 如果为true,表示已PRE_BOOT_COMPLETED已经处理完毕,确切的说是已经检查完毕,因为在派发该广播之前,要检查是否已经向接收者派发过一次该广播了; 之所以该变量取名为update,是因为该广播的设计与系统升级后的操作有关;
    • mWaitingUpdate: 默认为false; 如果有PRE_BOOT_COMPLETED的接收者,而且之前没有处理过该广播,则这个变量会被true,直到广播处理完成后才被重新置成false。

    经过这3步mSystemReady就被设置为true了,再次调用该systemReady()函数,就会进入第1步的逻辑

    在AMS完全准备就绪之前,就可能有一些进程已经启动,在这里需要进行一个检查,如果非peristent的进程先于AMS启动,那么就需要杀掉这些进程。 注意,这里所指的进程是AMS管理的进程(系统进程和应用进程),Native进程并不在AMS的管辖范围。

    在该代码片段的最后,输出了一行Event Log:

    boot_process_ams_ready: xxx
    

    进行日志分析时,可以根据这行日志信息判定AMS已经准备就绪,其他应用进程可以启动了。因为只有当AMS就绪后,才能开始管理应用进程。

    AMS准备就绪是个关键节点,在此之后,很多其他服务就可以开始进入准备就绪的状态了,其实就是调用这些系统服务的systemReady()函数。

    systemReady()函数的最后部分,一部分工作是发送通知:“当前用户已经进入使用状态了”; 另一部分工作就是启动桌面,有两处关键调用: startHomeActivityLocked()和mStackSupervisor.resumeTopActivitiesLocked()

    至此,systemReady()函数的执行逻辑已经分析完了,一共经历了4个步骤

    • 片段1: 主要处理PRE_BOOT_COMPLETED广播;
    • 片段2: 杀掉在AMS准备就绪之前就已经启动的进程。因为这些进程要被AMS管理起来,需要在AMS准备就绪之后才启动;
    • 片段3: 主要任务是通知其他系统服务进入就绪状态。在AMS就绪完毕后,从SettingsProvider和本地文件中(urigrants.xml)读取一些配置信息。通知其他系统服务进入就绪状态,启动SystemUi;
    • 片段4: 主要任务是启动桌面。

    systemReady()函数调用完成之后,桌面就可见了,用户就真正见到了Android系统的可操作界面

    3.4 发送ACTION_BOOT_COMPLETED广播

    在桌面启动后,桌面进程主线程的消息队列进入空闲状态,此时会发起跨进程调用AMS.activityIdle(),紧接着会引发下面的调用关系:

    AMS.activityIdle()
    └── ActivityStackSupervisor.activityIdleInternalLocked()
        └── ActivityStackSupervisor.checkFinishBootingLocked()
            └── AMS.postFinishBooting() // 向主线程抛出FINISH_BOOTING_MSG消息
                └── AMS.MainHandler.handleMessage(FINISH_BOOTING_MSG)
                    └── AMS.finishBooting()

    AMS.finishBooting()函数主要进行ABI检查,注册一些系统广播,启动之前被抑制启动的进程,发送ACTION_BOOT_COMPLETED广播,调度性能统计功能。

    总结:

       系统进程启动过程中与AMS相关的逻辑,总体而言,可以分为三步:

    1. 初始化系统进程的运行环境。所谓运行环境,就是指的Context,Context蕴含了代码执行过程中所需要的信息,包括进程信息、包信息、资源信息等等。Android有意弱化进程的概念,强化Context的概念,在Android编程时,一定避免不了与Context打交道。对于系统进程而言,Context有一定的特殊性,所以单独造了一个SystemContext。

    2. 初始化AMS对象。AMS对象在系统进程构建,作为最重要的系统服务,AMS初始化要做的事情非常多。由于各种系统服务耦合在一块,相互影响,Android设计了“系统进程启动阶段”这个概念,就像一个简单的状态机,只有进入的某个阶段,才能做某些操作。譬如,只有进入PHASE_ACTIVITY_MANAGER_READY,AMS才能正常工作,这时才可以进行派发广播、管理进程等操作。

      因为系统进程也在AMS的管辖范围之内,所以,AMS对象构建后有一个重要的任务,就是设置系统进程的一些属性。这时,会将第一个启动的应用frameworks-res.apk的信息装载到系统进程中,创建一个系统进程的ProcessRecord对象以便AMS管理。

    3. AMS初始化的配套工作。这里所谓配套工作是指,系统要完全运行起来,还需要经由AMS进行一系列的运作:系统设置SettingsProvider会经由AMS装载到系统进程中;其他系统服务在AMS准备就绪后,也会进入就绪状态,表示可以正常工作;桌面会经由AMS启动,最终ACTION_BOOT_COMPLETED广播发出。

    参考文章:http://duanqz.github.io/2016-07-15-AMS-LaunchProcess#3-%E6%80%BB%E7%BB%93

  • 相关阅读:
    jQuery选择器
    安装ASP.NET4.0
    自动换行的布局
    一个封装不错的 TcpClient 类
    goland 中国 caisy qq Czx123456
    由于google被墙, 一些go语言包不能下载, 可以通过Chome浏览器到这个网址下载
    在Windows 操作系统中, MySql 如何设置, 允许表名支持大小写
    websocket 的客户端 websocket-sharp
    将一个C++的AES加密算法(有向量的)翻译成C#
    AES 加密问题
  • 原文地址:https://www.cnblogs.com/krislight1105/p/10305126.html
Copyright © 2020-2023  润新知