Skip to content

Latest commit

 

History

History
155 lines (77 loc) · 12.2 KB

4.ActivityManagerService简介.md

File metadata and controls

155 lines (77 loc) · 12.2 KB

4.ActivityManagerService简介

ActivityManagerService简称AmS,是Android内核的三大核心功能之一,另外两个是WindowManagerService和View。Activity的管理实际上由三个主要类完成,分别是AmS、ActivityRecord及ActivityTask。AmS内部用ActivityRecord来表示一个Activity对象。ActivityStack是一个描述Task的类,所有和Task相关的数据以及控制都由ActivityStack来实现。

AmS所提供的主要功能包括以下几项:

  • 统一调度各应用程序的Activity。应用程序要运行Activity,会首先报告给AmS,然后由AmS决定该Activity是否可以启动,如果可以,AmS再通知应用程序运行指定的Activity。换句话说,运行Activity是各应用程序的内政,AmS并不干预,但是AmS必须知道应用进程都运行了哪些Activity。
  • 内存管理。Android官方声称,Activity退出后,其所在的进程并不会被立即杀死,从而下次在启动该Activity时能够提高启动速度。这些Activity只有当系统内存紧张时,才会被自动杀死,应用程序不用关心这个问题。而这些正是AmS中完成的。
  • 进程管理。AmS向外提供了查询系统正在运行的进程信息的API。

Activity调度机制

在Android中,Activity调度的基本思路是:各应用进程要启动新的Activity或者停止当前的Activity,都要首先报告给AmS,而不能“擅自处理”。AmS在内部为所有应用程序都做了记录,当AmS接到启动或停止的报告时,首先更新内部记录,然后再通知相应客户进程运行或者停止指定的Activity。由于AmS内部有所有Activity的记录,也就理所当然地能够调度这些Activity,并根据Activity和系统内存的状态自动杀死后台的Activity。

具体的讲,启动一个Activity有以下几种方式:

  • 在应用程序中调用startActivity()启动指定的Activity。
  • 在Home程序中单击一个应用图标,启动新的Activity。
  • 按Back键,结束当前Activity,自动启动上一个Activity。
  • 长按Home键,显示出当前任务列表,从中选择一个启动。

这四种启动方式的主体处理流程都会按照第一种启动方式运行,后三种方式只是在前端消息处理上各有不同。

AmS中定义了几个重要的数据类,分别用来保存进程(Process)、活动(Activity)和任务(Task)。

进程数据类ProcessRecord

ProcessRecord记录一个进程中的相关信息,该类中内部变量可以分为三个部分:

  • 进程文件信息:也就是与该进程对应的APK文件的内部信息,例如ApplicationInfo、processName等。
  • 该进程的内存状态信息:这些信息将用于Linux系统的Out Of Memory情况的处理,当发生系统内部不够用时,Linux系统会根据进程的内存状态信息,杀掉优先级比较低的进程。
  • 进程中包含的Activity、Provider、Service等:如ArrayList activities;

HistoryRecord数据类

AmS中使用HistoryRecord数据类来保存每个Activity的信息,这里非常奇怪,Activity本身也是一个类,为什么还要用HistoryRecord来保存Activity的信息,而不是直接用Activity呢?这是因为Activity是具体的功能类,这就好比每一个读者都是一个Activity,而学校要为每一个读者建立一个档案,这些档案中并不包含每个读者具体的学习能力,而只是学生的籍贯信息、姓名、出生日期等,HistoryRecord就是AmS为每一个Activity建立的档案,该数据类中的变量主要包含两部分:

  • 环境信息:该Activity的工作环境,比如隶属于哪个Package,所在的进程名称、文件路径、数据路径、图标主题等。
  • 运行状态信息:比如idle、stop、finishing等,这些变量一般为boolean类型,这些状态值与应用程序中的onCreate、onPause、onStart等状态有所不同。

HistoryRecord类也是一个Binder,它基于IApplication.Stub类,因此它可以被IPC调用,一般是在WmS中进行该对象的IPC调用。

TaskRecord类

AmS中使用任务的概念确保Activity启动和退出的顺序。比如如下启动流程,A、B、C分别代表三个应用程序,数字1、2、3分别代表该应用中的Activity。

A1 -> A2 -> A3 -> B1 -> B2 -> C1 -C2,此时应该处于C2,如果AmS中没有任务的概念,此时又要从C2启动B1,那么会存在以下两个问题:

  • 虽然程序上是要启动B1,但是用户可能期望启动B2,因为B1和B2是两个关联的Activity,并且B2已经运行于B1之后。如何提供给程序员一种选择,虽然指定启动B1,但如果B2已经运行,那么就启动B2.
  • 假设已经成功从C2跳转到B2,此时如果用户按Back键,是应该回到B1呢,还是应该回到C2?

任务概念的引入正是为了解决以上两个问题,HistoryRecord中包含一个init task变量,保存该Activity所属哪个任务,程序员可以使用Intent.FLAG_NEW_TASK标识告诉AmS为启动的Activity重新创建一个Task。

有了Task的概念后,以上情况就会是这样:

虽然程序明确指定从C2启动到B1,程序员可以在intent的FLAG中添加NEW_TASK标识,从而使得AmS会判断B1是否已经在mHistory中。如果在,则找到B1所在的Task,并从该Task中的最上面的Activity出运行,此处也就是B2.当然,如果程序的确要启动B1,那么就不要使用NEW_TASK标识,使用的话,mHistory中会有两个B1记录,隶属于不同的Task。

TaskRecord类内部变量如下:

  • taskId:每一个任务对应一个Int型的标识
  • intent:创建该任务对应的intent
  • numActivities:该任务中的Activity数目

TaskRecord中并没有该任务中所包含的Activity的列表,比如ArrayList或者HistoryRecord[]之类的变量,这意味着不能直接通过任务id找到其所包含的Activity。

AmS中调度相关常量

  • MAX_ACTIVITIES = 20;

    系统只能有一个Activity处于执行状态,对于非执行状态的Activity,AmS会在内部暂时缓存起来,而不是立即杀死,但如果后台的Activity超过该常量,则会强制杀死一些优先级较低的Activity。

  • PAUSE_TIMEOUT = 500;

    当AmS通知应用程序暂停指定的Activity时,AmS的忍耐是有限的,因为只有500毫秒,如果应用程序在该常量时间内还没有暂停,AmS会强制暂停并关闭该Activity。这就是为什么不能在onPause()中做过多事情的原因。

  • LAUNCH_TIMEOUT = 10 * 1000:当AmS通知应用程序启动某个Activity时,如果超过10s,AmS就会放弃

  • PROC_START_TIMEEOUT = 10 * 1000:当AmS启动某个客户进程后,客户进程必须在10秒之内报告AmS自己已经启动,否则AmS会认为指定的客户进程不存在。

等待序列

由于AmS采用Service机制运作,所有的客户进程要做什么事情,都要先请求AmS,因此,AmS内部必须有一些消息序列保存这些请求,并按顺序依次进行相应的操作:

  • final ArrayList mHistory = new ArrayList();

    这是最最重要的内部变量,该变量保存了所有正在运行的Activity,所谓正在运行是指该HistoryRecord的finishing状态为true。比如当前和用户交互的Activity属于正在运行,从A1启动到A2,尽管A1看不见了,但是仍然是正在运行。从A2按Home键回到桌面,A2也是正在运行,但如果从A2按Back键回到A1,这时A2就不是正在运行状态了,它会从mHistory中删除掉 。

  • private final ArrayList mLRUActivities = new ArrayList();

    LRU代表Latest Recent Used,即最近所用的Activity的列表,它不像mHistory仅保存正在运行的Activity,mLRUActirity会保存所有过去启动过的Activity。

  • final ArrayList mPendingActivityLaunches = new ArrayList();

    当AmS内部还没有准备好时,如果客户进程请求启动某个Activity,那么会被暂时保存到该变量中,这也就是pending的含义。这中情况一般发生在系统启动时,系统进程会查询系统中所有属性为Persisitant的客户进程,此时由于AmS也正在启动,因此,会暂时保存这些请求。

  • final ArrayList mStoppingActivitiies;

    在AmS的设计中,有这样一个理念:优先启动,其次再停止。即当用户请求启动A2时,如果A1正在运行,AmS首先会暂停A1,然后启动A2.当A2启动后再停止A1.在这个过程中,A1会被临时保存到mStoppingActivities中,直到A2启动后并处于空闲时,再回过头来停止mStoppingActivities中保存的HistoryRecord列表。

  • final ArrayList mFinishingActivities;

    和mStoppingActivities类似,当AmS认为某个Activity已经处于finish状态时,不会立即杀死该Activity,而是会保存到该变量中,直到超过系统设定的警戒线后,才去回收该变量中的Activity。

  • HistoryRecord mPausingActivity

    正在暂停的Activity,该变量只有在暂停某个Activity时才有值,代表正在暂停的Activity

  • HistoryRecord mResumedActivity

    当前正在运行的Activity,这里的正在运行不一定是正在与用户交互。比如当用户请求执行A2时,当前正在运行的A1,此时AmS会首先暂停A1,而在暂停的过程中,AmS会通知WmS暂停获取用户消息,而此时mResumedActivity依然是A1.

  • HistoryRecord mFocusedActivity

    这里的Focus并非是正在和用户交互,而是AmS通知WmS应该和用户交互的Activity,而在WmS真正处理这个消息之前,用户还是不能和该Activity交互。

  • HistoryRecord mLastPausedActivity

    上一次暂停的Activity

当系统内存低时,AmS会要求客户端释放内存,而可能会释放Surface对应的内存,而这是由WmS具体完成的。当WmS释放了Surface内存后,该Surface对应的窗口就无效了,则该窗口对应的Activity也就无效了,则Activity所在的进程也就无效了。

startActiviy()启动流程

  • 调用startActivity()方法后,通知到AmS,AmS收到请求startActivity()后,会首先暂停当前的Activity,因此这时候AmS需要判断mResumeActivity是否为空,也就是当前有没有正在运行的activity。一般情况下,该值都不会为空。
  • 如果为空的话继续往下走,如果不为空的话,AmS会通知当前mResumeActivity对应的Activity所在的进程暂停,然后AmS就不管了,当那个进程暂停完后会报告AmS,这时AmS开始执行completePaused()。该方法中会去检查要启动的目标Activity是否存在mHistory列表中,如果存在说明目标进程还在运行,只是目标Activity处于stop状态,还没有finish,所以会通知B进程则通过handleResumeActivity()方法来resume目标Activity。
  • 如果不存在那需要去检查目标Activity所在的进程是否存在。如果不存在则必须首先启动对应的进程。这时AmS调用Process进程类启动一个新的进程,新的进程会从ActivityThread的main()函数处开始执行,当对应进程启动后,B进程会报告AmS自己已经启动,于是执行AmS的attachApplication()方法,该方法可理解为B进程请求AmS给自己安排(attach)一个具体要执行的Activity,此时AmS继续调用resumeTopActivity(),通知B进程执行指定的Activity。
  • 首先判断目标HistoryRecord在B进程中不存在,则B调用handleLaunchActivity()创建一个该Activity实例。如果已经存在,则调用handleResumeActivity()恢复已有的Activity运行。这个逻辑的意思就是说,在ActivityThread中可以存在同一个Activity的多个实例,对应了AmS中mHistory的多个HistoryRecord对象。在一般情况下,当调用startActivity的FLAG为NEW_TASK时,AmS会首先从mHIstory中找到指定Activity所在的Task,然后启动Task中的最后一个Activity。如果FLAG不为NEW_TASK,那么AmS会在当前Task中重新创建一个HistoryRecord。