Android四大组件之Activity--管理方式

1. 概览

Activity的管理有静态和动态两层涵义:java

  • 静态是指Activity的代码组织结构,即Application中声明的Activity的集合,这些Activity被组织在一个APK中,有特定的包名。 在编写应用程序时,Activity对应到用户界面,它定义了用户界面的布局、交互行为、启动方式等,最重要的,是Activity的生命周期函数。 在应用进程看来,只须要按照Android定义的规范,实现生命周期函数的具体逻辑便可,全部的用户界面都遵循同一个规范。 编写完一个应用程序的全部用户界面,就算是完成了Activity的静态管理。android

  • 动态是指Activity的运行调度方式,即Activity生命周期的执行过程当中,内部数据结构的变化,Android对全部Activity进行统一管理。 在一个应用程序安装时,系统会解析出APK中全部Activity的信息,当显示APK中的用户界面时,就须要调度Activity的生命周期函数了。 系统进程(system_process)中维护了全部Activity的状态,管理中枢就是ActivityManagerService,Android为此作了精密的设计,采用 栈 做为基本的数据结构。git

本文分析的Activity管理方式属于动态这个层面,也就是系统进程中针对Activity的调度机制。github

2. Activity管理的基础

Activity的管理离不开基础的数据结构以及它们之间的相互关联, 因此,笔者会从基础的数据结构出发,分析类的属性和行为,并结合一些场景进行源码分析; 进一步,会分析各个类之间关联关系的构建过程。 这样一来,整个Activity管理运转的模型就清楚了,这个模型承载的不少业务,本文不会具体展开, 在Android四大组件之Activity–启动过程一文中,笔者会再详细介绍一种典型的业务。shell

2.1 数据结构

Activity管理相关的数据结构包括:数组

这些数据结构都是Java类,它们都属于系统进程的范畴,即对象的构建和销毁都在系统进程中完成,笔者将从类的属性和行为这两个角度来分析类的职能。 Android有一些约定俗成的函数命名方式,与Activity管理相关不少函数都会带有Locked后缀,表示这些函数须要进行多线程同步操做(synchronized),它们会读/写一些多线程共享的数据,读者在分析代码的时候能够适当关注。浏览器

先上一张数据结构的概览图:数据结构

Maintenace Guideline

图中的方框能够理解为一个中包含关系:譬如一个TaskRecord中包含多个ActivityRecord; 图中的链接线能够理解为等价关系,譬如同一个ActivityRecord会被TaskRecord和ProcessRecord引用,二者是从不一样维度来管理ActivityRecord。多线程

  • ActivityRecord是Activity管理的最小单位,它对应着一个用户界面;
  • TaskRecord也是一个栈式管理结构,每个TaskRecord均可能存在一个或多个ActivityRecord,栈顶的ActivityRecord表示当前可见的界面;
  • ActivityStack是一个栈式管理结构,每个ActivityStack均可能存在一个或多个TaskRecord,栈顶的TaskRecord表示当前可见的任务;
  • ActivityStackSupervisor管理着多个ActivityStack,但当前只会有一个获取焦点(Focused)的ActivityStack;
  • ProcessRecord记录着属于一个进程的全部ActivityRecord,运行在不一样TaskRecord中的ActivityRecord多是属于同一个 ProcessRecord。

ActivityRecord

ActivityRecord是AMS调度Activity的基本单位,它须要记录AndroidManifest.xml中所定义Activity的静态特征,同时, 也须要记录Activity在被调度时的状态变化,所以ActivityRecord这个类的属性比较多。app

属性 描述
ActivityInfo 从<activity>标签中解析出来的信息,包含launchMode,permission,taskAffinity等
mActivityType Activity的类型有三种:APPLICATION_ACTIVITY_TYPE(应用)、HOME_ACTIVITY_TYPE(桌面)、RECENTS_ACTIVITY_TYPE(最近使用)
appToken 当前ActivityRecord的标识
packageName 当前所属的包名,这是由<activity>静态定义的
processName 当前所属的进程名,大部分状况都是由<activity>静态定义的,但也有例外
taskAffinity 相同taskAffinity的Activity会被分配到同一个任务栈中
intent 启动当前Activity的Intent
launchedFromUid 启动当前Activity的UID,即发起者的UID
launchedFromPackage 启动当前Activity的包名,即发起者的包名
resultTo 在当前ActivityRecord看来,resultTo表示上一个启动它的ActivityRecord,当须要启动另外一个ActivityRecord,会把本身做为resultTo,传递给下一个ActivityRecord
state ActivityRecord所处的状态,初始值是ActivityState.INITIALIZING
app ActivityRecord的宿主进程
task ActivityRecord的宿主任务
inHistory 标识当前的ActivityRecord是否已经置入任务栈中
frontOfTask 标识当前的ActivityRecord是否处于任务栈的根部,便是否为进入任务栈的第一个ActivityRecord
newIntents Intent数组,用于暂存尚未调度到应用进程Activity的Intent

因为ActivityRecord是一个最基本的数据结构,因此其行为相对较少,大都是一些用于断定和更新当前ActivityRecord状态的函数:

行为 描述
putInHistory(), takeFromHistory(), isInHistory() 基于inHistory属性,来断定和更新ActivityRecord是否在任务栈的状态值
isHomeActivity(), isRecentsActivity(), isApplicationActivity() 基于mActivityType属性,断定Activity的类型
setTask() 设置ActivityRecord的宿主任务
deliverNewIntentLocked() 向当前ActivityRecord继续派发Intent。在一些场景下,位于任务栈顶的ActivityRecord会继续接受新的Intent(譬如以singleTop方式启动的同一个Activity),这时候,会触发调度Activity.onNewIntent()函数
addNewIntentLocked() 若是Intent没有派发到应用进程,则经过该函数往newIntents数组中添加一个元素。

要理解ActivityRecord这个数据结构,能够从其构造函数出发,理解其属性在什么场景下会发生变化。 每次须要启动一个新的Activity时,都会构建一个ActivityRecord对象,这在ActivityStackSupervisor.startActivityLocked()函数中完成,构造一个ActivityRecord要传入的参数是至关多的:

ActivityRecord(ActivityManagerService _service, ProcessRecord _caller, int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType, ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified, ActivityStackSupervisor supervisor, ActivityContainer container, Bundle options) { service = _service; // AMS对象 appToken = new Token(this); //appToken能够进行跨进程传递,标识一个AR对象 info = aInfo; //从AndroidManifest.xml中解析获得的Activity信息 launchedFromUid = _launchedFromUid; //譬如从浏览器启动当前AR,那么该属性记录的就是浏览器的UID launchedFromPackage = _launchedFromPackage; userId = UserHandle.getUserId(aInfo.applicationInfo.uid); intent = _intent; //启动当前AR的Intent shortComponentName = _intent.getComponent().flattenToShortString(); resolvedType = _resolvedType; componentSpecified = _componentSpecified; configuration = _configuration; resultTo = _resultTo; //记录上一个AR对象 resultWho = _resultWho; //reslutTo的字符串标识 requestCode = _reqCode; //上一个AR对象设定的Request Code state = ActivityState.INITIALIZING; //AR的状态,Activity调度时发生改变 frontOfTask = false; //是否处于Task的根部,调整任务栈中AR顺序时,可能发生改变 launchFailed = false; stopped = false; //pause操做完成状态位 delayedResume = false; finishing = false; //stoped到finished之间的过渡状态位 configDestroy = false; keysPaused = false; //若是置为true,则当前AR再也不接受用户输入 inHistory = false; //将AR压入任务栈后,该状态位被置为true visible = true; waitingVisible = false; nowVisible = false; idle = false; hasBeenLaunched = false; mStackSupervisor = supervisor; mInitialActivityContainer = container; if (options != null) { pendingOptions = new ActivityOptions(options); mLaunchTaskBehind = pendingOptions.getLaunchTaskBehind(); } haveState = true; if (aInfo != null) { //根据aInfo给realActivity, taskAffinity, processName等属性赋值 ... } else { //没有aInfo的状况下,赋予默认值 realActivity = null; taskAffinity = null; stateNotNeeded = false; appInfo = null; processName = null; packageName = null; fullscreen = true; noDisplay = false; mActivityType = APPLICATION_ACTIVITY_TYPE; immersive = false; } }

TaskRecord

TaskRecord的职责是管理多个ActivityRecord,本文所述的任务、任务栈指的就是TaskRecord。 启动Activity时,须要找到Activity的宿主任务,若是不存在,则须要新建一个,也就是说全部的ActivityRecord都必须有宿主。 TaskRecord与ActivityRecord是一对多的关系,TaskRecord的属性中包含了ActivityRecord的数组; 同时,TaskRecord还须要维护任务栈自己的状态。

属性 描述
taskid TaskRecord的惟一标识
taskType 任务栈的类型,等同于ActivityRecord的类型,是由任务栈的第一个ActivityRecord决定的
intent 在当前任务栈中启动的第一个Activity的Intent将会被记录下来,后续若是有相同的Intent时,会与已有任务栈的Intent进行匹配,若是匹配上了,就不须要再新建一个TaskRecord了
realActivity, origActivity 启动任务栈的Activity,这两个属性是用包名(CompentName)表示的,real和orig是为了区分Activity有无别名(alias)的状况,若是AndroidManifest.xml中定义的Activity是一个alias,则此处real表示Activity的别名,orig表示真实的Activity
affinity TaskRecord把Activity的affinity记录下来,后续启动Activity时,会从已有的任务栈中匹配affinity,若是匹配上了,则不须要新建TaskRecord
rootAffinity 记录任务栈中最底部Activity的affinity,一经设定后就再也不改变
mActivities 这是TaskRecord最重要的一个属性,TaskRecord是一个栈结构,栈的元素是ActivityRecord,其内部实现是一个数组mActivities
stack 当前TaskRecord所在的ActivityStack

TaskRecord的行为侧重在TaskRecord自己的管理:增/删/改/查任务栈中的元素。

行为 描述
getRootActivity(), getTopActivity() 任务栈有根部(Root)和顶部(Top),能够经过这两个函数分别获取到根部和顶部的ActivityRecord。获取的过程就是对TaskRecord.mActivities进行遍历,若是ActivityRecord的状态不是finishing,就认为是有效的ActivityRecord
topRunningActivityLocked() 虽然也是从顶至底对任务栈进行遍历获取顶部的ActivityRecord,但这个函数同getTopActivity()有区别:输入参数notTop,表示在遍历的过程当中须要排除notTop这个ActivityRecord;
addActivityToTop(), addActivityAtBottom() 将ActivityRecord添加到任务栈的顶部或底部
moveActivityToFrontLocked() 该函数将一个ActivityRecord移至TaskRecord的顶部,实现方法就是先删除已有的,再在栈顶添加一个新的
setFrontOfTask() ActivityRecord有一个属性是frontOfTask,表示ActivityRecord是否为TaskRecord的根Activity。该函数设置TaskRecord中全部ActivityRecord的frontOfTask属性,从栈底往上开始遍历,第一个不处于finishing状态的ActivityRecord的frontOfTask属性置成true,其余都为false
performClearTaskLocked() 清除TaskRecord中的ActivityRecord。当启动Activity时,用了Intent.FLAG_ACTIVITY_CLEAR_TOP参数,那么在宿主任务中,待启动ActivityRecord之上的其余ActivityRecord都会被清除

仅仅把类的属性和行为罗列出来,固然不足以理解TaskRecord的工做原理。 接下来,将深刻部分函数的代码,分析TaskRecord在一些场景下的具体执行逻辑。

场景 1 启动一个Activity时,一般须要将ActivityRecord压入任务栈顶,addActivityToTop()就是为此功能设计:

void addActivityToTop(ActivityRecord r) { addActivityAtIndex(mActivities.size(), r); }

将ActivityRecord压入栈顶,其实就是在mActivities数组末尾添加一个元素,因此,实际压入栈顶的操做是由addActivityAtIndex()完成:

void addActivityAtIndex(int index, ActivityRecord r) { // 1. 移除已有的ActivityRecord对象 if (!mActivities.remove(r) && r.fullscreen) { numFullscreen++; } // 2. 根据任务栈是否为空,设置状态 if (mActivities.isEmpty()) { taskType = r.mActivityType; isPersistable = r.isPersistable(); mCallingUid = r.launchedFromUid; mCallingPackage = r.launchedFromPackage; maxRecents = Math.min(Math.max(r.info.maxRecents, 1), ActivityManager.getMaxAppRecentsLimitStatic()); } else { r.mActivityType = taskType; } // 3. 在指定的位置添加ActivityRecord mActivities.add(index, r); // 4. 更新任务栈关联的Intent updateEffectiveIntent(); ... }

该函数会通过如下处理过程:

  1. 移除任务栈中已有的ActivityRecord对象,即任务栈中不会出现两个一样的ActivityRecord对象。此处须要注意,两次启动同一个Activity,是会产生两个不一样的ActivityRecord对象的;

  2. 若是任务栈为空,则设置任务栈的初始状态,不然,设置ActivityRecord的类型为任务栈的类型。因而可知,同一个任务栈中,全部ActivityRecord的类型都是同样的,并且是由任务栈的第一个ActivityRecord的类型决定的;

  3. 此处的位置就是任务栈顶,也就是mActivities属性的末尾;

  4. 任务栈中元素发生了变化,因此须要更新任务栈关联的Intent,这是经过调用updateEffectiveIntent()实现的,函数的具体逻辑,在场景 3中再行分析。

场景 2 当待显示的Activity压入任务栈后,就须要设置栈顶ActivityRecord的状态,这时候,会调用topRunningActivityLocked()函数来获取栈顶的元素,为了更好的分析topRunningActivityLocked()的使用场景,笔者把另外一个与其功能类似的函数getTopActivity()也列出来:

ActivityRecord getTopActivity() { for (int i = mActivities.size() - 1; i >= 0; --i) { final ActivityRecord r = mActivities.get(i); if (r.finishing) { continue; } return r; } return null; } ActivityRecord topRunningActivityLocked(ActivityRecord notTop) { for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) { ActivityRecord r = mActivities.get(activityNdx); // 除了要求ActivityRecord不是finishing状态之外,还要求不是当前给定输入的ActivityRecord if (!r.finishing && r != notTop && stack.okToShowLocked(r)) { return r; } } }

二者是从顶到底对任务栈进行遍历,但实现逻辑不一样,topRunningActivityLocked()接受一个输入参数notTop,在寻找时,要求排除notTop指定的ActivityRecord,一般,传入的notTop都是null,隐含的意思就是栈顶的ActivityRecord尚未被销毁。从函数命名topRunning,也能够看出其与getTop的区别:getTop无论栈顶的死活,拿到就好; topRunning要求拿到的必定是活的栈顶。

另外,topRunningActivityLocked()还有一个限制条件: ActivityRecord是能够被显示的(okToShow),这是经过ActivityStack.okToShowLocked()来断定的,主要是为了应多多用户或锁屏显示的Activity,通常状况下,函数返回值都为true。

场景 3 假定在启动一个Activity时,设置了Intent的FLAG_ACTIVITY_REORDER_TO_FRONT,表示要将Activity重排序到任务栈顶。 若是目标的Activity在任务栈中已经启动过,则须要将其挪至栈顶。譬如目标任务栈从底到顶是 A - B - C, 而后,又以FLAG_ACTIVITY_REORDER_TO_FRONT启动了 A,那最终任务栈会变化为 B - C - A 。 这就会调用到moveActivityToFrontLocked()函数:

final void moveActivityToFrontLocked(ActivityRecord newTop) { mActivities.remove(newTop); mActivities.add(newTop); updateEffectiveIntent(); setFrontOfTask(); }

该函数须要调整任务栈中ActivityRecord的顺序,延用上述例子, A 将做为参数newTop。 首先,会将 A 从任务栈中移除; 而后,再将 A 添加到任务栈顶; 接着,会调用updateEffectiveIntent()函数来更新任务栈关联的Intent:

void updateEffectiveIntent() { final int effectiveRootIndex = findEffectiveRootIndex(); final ActivityRecord r = mActivities.get(effectiveRootIndex); setIntent(r); }

该函数会找到一个有效的Root Index,即任务栈底部的索引,根据这个索引值取出对应的ActivityRecord。 延续上述例子,B 会被调整为任务栈的根部ActivityRecord,经过调用setIntent()来设置当前任务栈关联的Intent为启动 B 的Intent,然而,这里可不止改变TaskRecord.intent这一个属性这个简单,与Taskrecord的发起者相关的属性值都要更改, 譬如mCallingUid,mCallingPackage都得更改成 B 的发起者:

void setIntent(ActivityRecord r) { setIntent(r.intent, r.info); mCallingUid = r.launchedFromUid; mCallingPackage = r.launchedFromPackage; }

这里还有一个重载的setIntent()函数,再也不展开分析了,只须要知道诸如affinity, realActivity等属性都会被重置便可。

更新完TaskRecord的Intent,再回到moveActivityToFrontLocked()函数中,须要继续更新任务栈的Front。 以前任务栈的Front是 A,在发生变化后, Front须要更新为 B,然而,TaskRecord并无一个属性用来记录当前的Front, 它是根据任务栈中每个ActivityRecord的frontOfTask属性来断定的:

final void setFrontOfTask() { boolean foundFront = false; final int numActivities = mActivities.size(); for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { // 从栈底往上遍历 final ActivityRecord r = mActivities.get(activityNdx); if (foundFront || r.finishing) { // 其余ActivityRecord的这个属性都置为false r.frontOfTask = false; } else { // 不为finishing状态,表示已经找到了front的ActivityRecord r.frontOfTask = true; foundFront = true; } if (!foundFront && numActivities > 0) { mActivities.get(0).frontOfTask = true; } }

该函数从底到顶对任务栈进行遍历,找到的第一个未结束(finishing = faulse)的ActivityRecord, 将其frontOfTask属性设置成true;其余全部ActivtyRecord的frontOfTask属性设置为false。

ActivityStack

ActivityStack的职责是管理多个任务栈(TaskRecord),它是一个栈式结构,栈中的元素是TaskRecord。 每一个Activity在特定的时刻都会有一个状态,譬如显示、销毁等, 在应用进程看来,这些状态的变化就是在执行Activity的生命周期函数; 在系统进程看来,这些状态的变化都须要通过ActivityStack来驱动。 Activity的状态是经过ActivityState这个枚举类来定义的:

enum ActivityState { INITIALIZING, RESUMED, PAUSING, PAUSED, STOPPING, STOPPED, FINISHING, DESTROYING, DESTROYED }

从INITIALIZING到DESTROYED,所定义状态值示意了Activity生命周期的走向。

属性 描述
stackId 每个ActivityStack都有一个编号,从0开始递增。编号为0,表示桌面(Launcher)所在的ActivityStack,叫作Home Stack
mTaskHistory TaskRecord数组,ActivityStack栈就是经过这个数组实现的
mPausingActivity 在发生Activity切换时,正处于Pausing状态的Activity
mResumedActivity 当前处于Resumed状态的ActivityRecord
mStacks ActivityStack会绑定到一个显示设备上,譬如手机屏幕、投影仪等,在AMS中,经过ActivityDisplay这个类来抽象表示一个显示设备,ActivityDisplay.mStacks表示当前已经绑定到显示设备的全部ActivityStack。当执行一次绑定操做时,就会将ActivityStack.mStacks这个属性赋值成ActivityDisplay.mStacks,不然,ActivityStack.mStacks就为null。简而言之,当mStacks不为null时,表示当前ActivityStack已经绑定到了一个显示设备

Activity状态的变迁,不只仅是给ActivityRecord.state赋一个状态值那么简单,ActivityStack要对栈进行调整:以前的Activity要销毁或者挪到后台,待显示的Activity要挪到栈顶,这一调整,涉及到的工做就多了。

行为 描述
findTaskLocked() 该函数的功能是找到目标ActivityRecord(target)所在的任务栈(TaskRecord),若是找到,则返回栈顶的ActivityRecord,不然,返回null
findActivityLocked() 根据Intent和ActivityInfo这两个参数能够获取一个Activity的包名,该函数会从栈顶至栈底遍历ActivityStack中的全部Activity,若是包名匹配成功,就返回
moveToFront) 该函数用于将当前的ActivityStack挪到前台,执行时,调用ActivityStack中的其余一些断定函数
isAttached() 用于断定当前ActivityStack是否已经绑定到显示设备
isOnHomeDisplay() 用于断定当前是否为默认的显示设备(Display.DEFAULT_DISPLAY),一般,默认的显示设备就是手机屏幕
isHomeStack() 用于断定当前ActivityStack是否为Home Stack,即断定当前显示的是否为桌面(Launcher)
moveTaskToFrontLocked() 该函数用于将指定的任务栈挪到当前ActivityStack的最前面。在Activity状态变化时,须要对已有的ActivityStack中的任务栈进行调整,待显示Activity的宿主任务须要挪到前台
insertTaskAtTop() 将任务插入ActivityStack栈顶

ActivityStack还有不少与迁移Activity状态相关的行为: startActivityLocked(), resumeTopActivityLocked(), completeResumeLocked(), startPausingLocked(), completePauseLocked(), stopActivityLocked(), activityPausedLocked(), finishActivityLocked(), activityDestroyedLocked(), 它们与Activity的生命周期调度息息相关,在Android四大组件之Activity–启动过程一文中,会再详细分析这几个函数的实现逻辑,本文仍是经过一个简单的场景来分析ActivityStack的行为。

场景 1 以singleTask的方式启动一个处于后台的Activity,那么,就须要将Activity挪到前台。怎么挪呢?

第一步,findTaskLocked(): 找到Activity所在TaskRecord;

ActivityRecord findTaskLocked(ActivityRecord target) { ... for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = mTaskHistory.get(taskNdx); ... final ActivityRecord r = task.getTopActivity(); ... final Intent taskIntent = task.intent; final Intent affinityIntent = task.affinityIntent; ... if (!isDocument && !taskIsDocument && task.rootAffinity != null) { if (task.rootAffinity.equals(target.taskAffinity)) { return r; } } else if (taskIntent != null && taskIntent.getComponent() != null && taskIntent.getComponent().compareTo(cls) == 0 && Objects.equals(documentData, taskDocumentData)) { return r; } else if if (affinityIntent != null && affinityIntent.getComponent() != null && affinityIntent.getComponent().compareTo(cls) == 0 && Objects.equals(documentData, taskDocumentData)) { return r } ... } return null; }

该函数的功能是找到target ActivityRecord所在的Task,若是找到,则返回Task栈顶的ActivityRecord,不然,返回null。 主体逻辑是对ActivityStack中的全部Task进行遍历,如下几种状况表示找到了ActivityRecord的宿主task:

  • Affinity相同。rootAffinity表示第一次启动该task时affinity值,若是一个ActivityRecord的taskAffinity属性与其相等, 那么这个task天然是ActivityRecord的宿主;
  • Intent的包名相同。
  • Affinity Intent的包名相同。

第二步,moveToFront(): 将TaskRecord所在的ActivityStack挪到前台;

final void moveToFront(String reason) { if (isAttached()) { if (isOnHomeDisplay()) { mStackSupervisor.moveHomeStack(isHomeStack(), reason); } mStacks.remove(this); mStacks.add(this); final TaskRecord task = topTask(); if (task != null) { mWindowManager.moveTaskToTop(task.taskId); } } }

首先,会有一些断定:

  • isAttached(): 只有在当前ActivityStack绑定到显示设备的状况下,才须要挪到;
  • isOnHomeDisplay(): 若是当前是默认的显示设备,则对HomeStack(桌面)进行挪动, 这涉及到对多个ActivityStack的操做,因此须要经过ActivityStackSupervisor完成;
  • isHomeStack(): 若是当前是HomeStack,则将其挪到先后; 不然,将HomeStack挪到后台

而后,就是对mStacks这个属性进行操做:在mStacks数组中,删除已有的ActivityStack对象,并添加一个新的,这样作其实达到了一个目的,前台的ActivityStacks处于mStacks末尾。

最后,调用WMS.moveTaskToTop()通知窗口的进行变化调整。

第三步, moveTaskToFrontLocked(): 将TaskRecord挪到ActivityStack的栈顶;

final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord source, Bundle options, String reason) { final int numTasks = mTaskHistory.size(); final int index = mTaskHistory.indexOf(tr); // 断定ActivityStack是否须要挪动任务栈 if (numTasks == 0 || index < 0) { ... return; } // 调整ActivityStack insertTaskAtTop(tr); moveToFront(reason); ... // 将栈顶的Activity置为Resumed状态 mStackSupervisor.resumeTopActivitiesLocked(); }

首先,会通过断定:若是当前的ActivityStack为空,或者不存在要挪动的任务,则不须要对当前ActivityStack进行调整;

而后,肯定目标任务在当前ActivityStack中,则对ActivityStack进行调整,将目标任务插入ActivityStack栈顶。

  • insertTaskAtTop(),会先将已有的目标任务删除,再从新添加到栈顶位置;
  • moveToFront(),在第二步中执行过一次,由于在某些场景下,只会调用moveToFront(),不会调用moveTaskToFrontLocked(); 一旦要将任务挪到ActivityStack栈顶,意味着ActivityStack也必定要挪到前台;

最后,将任务栈顶的Activity置为Resumed状态,这里是经过ActivityStackSupervisor完成的。由于可能同时存在多个显示设备,因此须要对多个ActivityStack进行操做。

Activity管理中有两个栈顶:一是ActivityStack的栈顶,它对应到要显示的TaskRecord; 二是TaskRecord的栈顶,它对应到要显示的Activity。简单来讲,当前显示的Activity必定是位于其所属的TaskRecord的栈顶,TaskRecord也必定位于其所属的ActivityStack的栈顶。

ActivityDisplay

Android支持多屏显示,在不一样的显示设备上能够有不一样的ActivityStack。

笔者一直在重述:全部的ActivityStack都是经过ActivityStackSupervisor管理起来的。 在ActivityStackSupervisor内部,设计了ActivityDisplay这个内部类,它对应到一个显示设备,默认的显示设备是手机屏幕。 ActivityStackSupervisor间接经过ActivityDisplay来维护多个ActivityStack的状态。 ActivityStack有一个属性是mStacks,当mStacks不为空时,表示ActivityStack已经绑定到了显示设备, 其实ActivityStack.mStacks只是一个副本,真正的对象在ActivityDisplay中。

属性 描述
mDisplayId 显示设备的惟一标识
mDisplay 获取显示设备信息的工具类,
mDisplayInfo 显示设备信息的数据结构,包括类型、大小、分辨率等
mStacks 绑定到显示设备上的ActivityStack
行为 描述
attachActivities() 将一个ActivityStack绑定到显示设备
setVisibleBehindActivity() 设置后台显示的Activity
moveHomeStack() 移动HomeStack

ActivityContainer

在ActivityStackSupervisor中,还设计了名为ActivityContainer的内部类。 该类是对ActivityStack的封装,至关于开了一个后门,能够经过adb shell am命令对ActivityStack进行读写操做,方便开发和调试。

ActivityStackSupervisor

ActivityStackSupervisor的职责是管理多个ActivityStack。

属性 描述
mHomeStack 主屏(桌面)所在ActivityStack
mFocusedStack 表示焦点ActivityStack,它可以获取用户输入
mLastFocusedStack 上一个焦点ActivityStack
mActivityDisplays 表示当前的显示设备,ActivityDisplay中绑定了若干ActivityStack。经过该属性就能间接获取全部ActivityStack的信息
行为 描述
setFocusedStack() 设置当前的焦点ActivityStack
adjustStackFocus()  
startHomeActivity() 启动桌面

ActivityStackSupervisor有不少与ActivityStack功能相似的行为,不过都是针对多个ActivityStack进行操做。 譬如findTaskLocked(), findActivityLocked(), topRunningActivityLocked(), ensureActivitiesVisibleLocked()等,

场景 1 在启动一个新的Activity时,须要设置当前的焦点,经过AMS.setFocusedActivityLocked()函数,就能设置一个 ActivityRecord为当前的焦点Activity:

final void setFocusedActivityLocked(ActivityRecord r, String reason) { if (mFocusedActivity != r) { mFocusedActivity = r; ... mStackSupervisor.setFocusedStack(r, reason + " setFocusedActivity"); if (r != null) { mWindowManager.setFocusedApp(r.appToken, true); } applyUpdateLockStateLocked(r); } ... }

该函数的逻辑很简单,若是当前的焦点(mFocusedActivity)不是待显示的(r),则须要更新焦点; 而后,就发起了其余函数调用。 这里,须要经过ActivityStackSupervisor完成对ActivityStack的管理,经过调用setFocusedStack()来设置当前的焦点Stack, 焦点Stack就是焦点Activity所属的ActivityStack。

void setFocusedStack(ActivityRecord r, String reason) { if (r != null) { final TaskRecord task = r.task; // 判断输入的ActivityRecord是否为HomeActivity boolean isHomeActivity = !r.isApplicationActivity(); if (!isHomeActivity && task != null) { isHomeActivity = !task.isApplicationTask(); } if (!isHomeActivity && task != null) { final ActivityRecord parent = task.stack.mActivityContainer.mParentActivity; isHomeActivity = parent != null && parent.isHomeActivity(); } moveHomeStack(isHomeActivity, reason); } }

只有前台的ActivityStack才能获取焦点,因此,该函数的功能就是要将待显示的Activity所在的ActivityStack挪到前台。 很重要的一个处理逻辑就是断定待显示的ActivityRecord的类型是否为HomeActivity,断定细节此处不表。结果是: 若是待显示的ActivityRecord类型为HomeActivity,则须要将HomeStack挪到前台; 不然,意味着要将HomeStack挪到后台。 挪动HomeStack,是经过moveHomeStack()这个函数实现的:

void moveHomeStack(boolean toFront, String reason) { // 1. 获取当前的Top Stack ArrayList<ActivityStack> stacks = mHomeStack.mStacks; final int topNdx = stacks.size() - 1; if (topNdx <= 0) { return; } ActivityStack topStack = stacks.get(topNdx); // 2. 断定HomeStack是否须要挪动 final boolean homeInFront = topStack == mHomeStack; if (homeInFront != toFront) { mLastFocusedStack = topStack; stacks.remove(mHomeStack); stacks.add(toFront ? topNdx : 0, mHomeStack); mFocusedStack = stacks.get(topNdx); } ... // 3. 断定当前AMS是否完成启动 if (mService.mBooting || !mService.mBooted) { final ActivityRecord r = topRunningActivityLocked(); if (r != null && r.idle) { checkFinishBootingLocked(); } } }
  1. 获取当前的Top Stack,其实就是获取mStacks这个数组最后的元素。mStacks这个属性在ActivityStack和ActivityDisplay中都见过,它们是同一个东西,ActivityStackSupervisor要管理的就是这个东西;

  2. 断定当前HomeStack是否须要挪动。有四种状况:
    • homeInFront = true, toFront = false: 表示HomeStack在前台,要将其挪到后台,则须要将HomeStack挪到mStacks的0号位置;
    • homeInFront = true, toFront = true: 表示HomeStack在前台,要将其挪到前台,则不须要对mStacks进行调整;
    • homeInFront = false, toFront = true: 表示HomeStack在后台,要将其挪到前台,则须要将HomeStack挪到mStacks的末尾;
    • homeInFront = false, toFront = false: 表示HomeStack在后台,要将其挪到后台,则不须要对mStacks进行调整。
  3. 判断当前AMS是否完成启动。若是当前是刚开机,AMS都还未启动完成,须要显示的Activity还处于idle状态,则须要发起一次是否启动完成的检查

ProcessRecord

AMS采用ProcessRecord这个数据结构来维护进程运行时的状态信息,当建立系统进程(system_process)或应用进程的时候,就会经过AMS初始化一个ProcessRecord。

属性 描述
BatteryStats 电量统计的接口
ApplicationInfo 系统进程的ApplicationInfo是从android包中解析出来的数据; 应用程序的ApplicationInfo是从AndroidManifest.xml中解析出来的数据
Process Name 进程名称
UID 进程的UID。系统进程的UID是1000(Process.SYSTEM_UID); 应用进程的UID是从10000(Process.FIRST_APPLICATION_UID)开始分配的
maxAdj, curAdj, setAdj 各类不一样的OOM Adjustment值
lastPss, lastPssTime 物理内存(PSS)相关,进程中有对象建立或销毁时,PSS相关的属性会被更新。
activities, services, receivers 进程中的Android组件,随着进程的运行,这些信息均可能须要更新。譬如Activity的启动时,ProcessRecord.activies会增长一个实例; 销毁时,对将对应的实例从activities删除
pkgList 进程中运行的包
thread 该属性是IApplicationThread类型的对象

ProcessRecord有“激活(Active)”和“非激活(Inactive)”两种状态,只有将ProcessRecord绑定到一个实际进程的时候,才是激活状态。 绑定成功后,thread属性就被赋值,表示ProcessRecord已经激活。 激活后,AMS就能够经过这个接口完成对应用进程的管理,譬如启动Activity、派发广播等。 将ProcessRecord绑定到应用进程的过程在Android四大组件之Activity–应用进程与系统进程的通讯一文中有详细的分析。

行为 描述
makeActive() 将ProcessRecord置成激活状态
makeInactive() 将ProcessRecord置成非激活状态
addPackage() 向ProcessRecord添加包

2.2 关联关系

Maintenace Guideline
  • AMS运行在SystemServer进程中。SystemServer进程启动时,会经过SystemServer.startBootstrapServices()来建立一个AMS的对象;
private void startBootstrapServices() { ... mActivityManagerService = mSystemServiceManager.startService( ActivityManagerService.Lifecycle.class).getService(); ... }
  • AMS经过ActivityStackSupervisor来管理Activity。AMS对象只会存在一个,在初始化的时候,会建立一个惟一的ActivityStackSupervisor对象;
public ActivityManagerService(Context systemContext) { ... mStackSupervisor = new ActivityStackSupervisor(this); ... }
  • ActivityStackSupervisor中维护了显示设备的信息。当有新的显示设备添加时,会建立一个新的ActivityDisplay对象;
public void handleDisplayAddedLocked(int displayId) { ... newDisplay = mActivityDisplays.get(displayId) == null; if (newDisplay) { ActivityDisplay activityDisplay = new ActivityDisplay(displayId); ... } ... }
  • ActivityStack与显示设备的绑定。当须要建立一个ActivityStack时,须要将其绑定到具体的显示设备。 ActivityStackSupervisor经过ActivityContainer这个内部类对ActivityStack进行了一层封装, 因此,会首先建立一个ActivityContainer对象;而后,经过ActivityContainer.attachToDisplayLocked()函数进行具体的绑定操做;
private int createStackOnDisplay(int stackId, int displayId) { ... ActivityContainer activityContainer = new ActivityContainer(stackId); mActivityContainers.put(stackId, activityContainer); activityContainer.attachToDisplayLocked(activityDisplay); return stackId; }
  • AMS维护了全部进程的信息ProcessRecord。当须要建立一个新的进程时, 会经过AMS.newProcessRecordLocked()函数来建立一个ProcessRecord对象, ProcessRecord对象都保存在AMS.mPidsSelfLocked这个属性中;
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess, boolean isolated, int isolatedUid) { ... return new ProcessRecord(stats, info, proc, uid); }
  • 经过ActivityStackSupervisor来建立ActivityRecord。当SystemServer进程收到来自应用进程的启动Activity请求时, 会经过ActivityStackSupervisor来建立一个ActivityRecord对象;
final int startActivityLocked(IApplicationThread caller, ...) { ... ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage, intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho, requestCode, componentSpecified, this, container, options); ... }
  • 在ActivityStack上建立TaskRecord。当须要建立新的任务栈时,就会经过ActivityStack对象来建立一个TaskRecord对象, 这样就创建了ActivityStack和TaskRecord的关联;
TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, boolean toTop) { TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession, voiceInteractor); addTask(task, toTop, false); return task; }
  • ActivityRecord的宿主TaskRecord。每个ActivityRecord都须要找到本身的宿主TaskRecord,经过ActivityRecord.setTask()函数 就能创建ActivityRecord和TaskRecord的关联;
void setTask(TaskRecord newTask, TaskRecord taskToAffiliateWith) { ... task = newTask; setTaskToAffiliateWith(taskToAffiliateWith); }
  • 进程中运行的Activity信息。Activity在应用进程中运行,AMS中记录了进程中全部运行的Activity的信息,在ActivityRecord建立后, 会经过ProcessRecord.addPackage()函数,在ProcessRecord中登记ActivityRecord的信息
void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) { ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true); ... app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode, ... }

3. Activity管理的延伸

在分析完Activity管理的基础数据结构及关联关系后,想必各位读者已经感觉到了Activity管理的复杂性。 如此庞大而精密的数据结构设计,是在什么背景下产生的呢?从已有的Activity设计中, 可否窥探出之后Android在Activity相关特性的发展方向呢?譬如多屏幕、多窗口的Activity显示。

笔者一直认为,研究Android源码不只仅是理解Android的内部运行机制,更重要的是体会出其背后的设计思想, 总结出一套解决同类问题的方法论,而后再到具体的软件开发中进行实践,哪怕不在Android平台下开发, 提炼出来的方法论仍然是受用的。

相关文章
相关标签/搜索