Android四大组件之Activity--管理方式
1. 概览
Activity的管理有静态和动态两层涵义:
-
静态是指Activity的代码组织结构,即Application中声明的Activity的集合,这些Activity被组织在一个APK中,有特定的包名。 在编写应用程序时,Activity对应到用户界面,它定义了用户界面的布局、交互行为、启动方式等,最重要的,是Activity的生命周期函数。 在应用进程看来,只需要按照Android定义的规范,实现生命周期函数的具体逻辑即可,所有的用户界面都遵循同一个规范。 编写完一个应用程序的所有用户界面,就算是完成了Activity的静态管理。
-
动态是指Activity的运行调度方式,即Activity生命周期的执行过程中,内部数据结构的变化,Android对所有Activity进行统一管理。 在一个应用程序安装时,系统会解析出APK中所有Activity的信息,当显示APK中的用户界面时,就需要调度Activity的生命周期函数了。 系统进程(system_process)中维护了所有Activity的状态,管理中枢就是ActivityManagerService,Android为此做了精密的设计,采用 栈 作为基本的数据结构。
本文分析的Activity管理方式属于动态这个层面,也就是系统进程中针对Activity的调度机制。
2. Activity管理的基础
Activity的管理离不开基础的数据结构以及它们之间的相互关联, 所以,笔者会从基础的数据结构出发,分析类的属性和行为,并结合一些场景进行源码分析; 进一步,会分析各个类之间关联关系的构建过程。 这样一来,整个Activity管理运转的模型就清楚了,这个模型承载的很多业务,本文不会具体展开, 在Android四大组件之Activity–启动过程一文中,笔者会再详细介绍一种典型的业务。
2.1 数据结构
Activity管理相关的数据结构包括:
这些数据结构都是Java类,它们都属于系统进程的范畴,即对象的构建和销毁都在系统进程中完成,笔者将从类的属性和行为这两个角度来分析类的职能。 Android有一些约定俗成的函数命名方式,与Activity管理相关很多函数都会带有Locked后缀,表示这些函数需要进行多线程同步操作(synchronized),它们会读/写一些多线程共享的数据,读者在分析代码的时候可以适当关注。
先上一张数据结构的概览图:
![Maintenace Guideline](http://duanqz.github.io/assets/images/activity/maintenace/1-activity-maintenace-structure.png)
图中的方框可以理解为一个中包含关系:譬如一个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这个类的属性比较多。
属性 | 描述 |
---|---|
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