我们从一个最常见的面试题开始:描述Activity的四种启动模式。
standard : Activity的默认启动模式,在这种模式下启动的activity可以被多次实例化,即在同一个任务栈中可以存在多个activity的实例,每个实例都会处理一个Intent对象。如果Activity A的启动模式为standard,并且A已经启动,在 A 中再次启动Activity A,会在 A 的上面再次启动一个 A 的实例,即当前的桟中的状态为A–>A。
singleTop:和standard模式很像,唯一的区别在于,如果一个以singleTop模式启动的activity的实例已经存在于任务桟的桟顶,那么再启动这个Activity时,不会创建新的实例,而是重用位于栈顶的那个实例,并且会调用该实例的onNewIntent()方法将Intent对象传递到这个实例中。
如果以singleTop模式启动的activity的一个实例已经存在与任务桟中,但是不在桟顶,那么它的行为和standard模式相同,也会创建多个实例。
singleTask:这种启动模式的Activity 会在其所在的任务栈中始终保持只有一个实例,当启动这个Activity的时候,系统会搜寻系统中现存的任务栈,如果没有任务栈中有该Activity的实例,则会创建这个Activity属于的任务栈,并正常创建该Activity 实例,否则会把这个任务栈调到前台,且会将任务栈中其实例以上的所有Activity出栈,并调用该实例的onNewIntent()方法将Intent对象传递到这个实例当中。
singleInstance:singleInstance是singleTask的特殊情况,总是在新的任务中开启,并且这个新的任务中有且只有这一个实例,也就是说被该实例启动的其他activity会自动运行于另一个任务中。当再次启动该activity的实例时,会重用已存在的任务和实例。并且会调用这个实例的onNewIntent()方法,将Intent实例传递到该实例中。同一时刻在系统中只会存在一个这样的Activity实例。
四种启动模式中,个人觉得最简单的是standard,最复杂的是singleTask。从这个问题,我们还能引申出更多相关的问题,比如常用的Intent启动Flag有哪些,都有什么作用,以及taskAffinity的含义是否知道。这些都可以在Google 官方文档《任务和返回栈》中找到答案。
本文想探讨的是在Framework层是如何实现这套机制的。先不看Android怎么实现的,我们自己来想想,首先我们需要一个类代表任务吧,就叫Task吧,每个Task中肯定要有一个保存其中Activity的List,就叫mActivities吧,这样还不行,系统里肯定不止一个Task,那么我们还有一个List来保存所有的Task,就叫mTasks吧,以上应该算是Model层。那我们还需要一个Controller,来调度所有这些Model。
再来看Android怎么实现的? 任务类是TaskRecord.java,其中的确有一个保存该任务中所有的Activity的变量:
//TaskRecord.java
/** List of all activities in the task arranged in history order */
final ArrayList mActivities;
和我们一开始想的不一样,不是Activity类型的ArrayList,而是ActivityRecord,那我们来看看这个类:
......
final ActivityManagerService service; // owner
final IApplicationToken.Stub appToken; // window manager token
final ActivityInfo info; // all about me
final ApplicationInfo appInfo; // information about activity's app
final int launchedFromUid; // always the uid who started the activity.
final String launchedFromPackage; // always the package who started the activity.
final int userId; // Which user is this running for?
final Intent intent; // the original intent that generated us
final ComponentName realActivity; // the intent component, or target of an alias.
final String shortComponentName; // the short component name of the intent
final String resolvedType; // as per original caller;
final String packageName; // the package implementing intent's component
final String processName; // process where this component wants to run
final String taskAffinity; // as per ActivityInfo.taskAffinity
final boolean stateNotNeeded; // As per ActivityInfo.flags
boolean fullscreen; // covers the full screen?
final boolean noDisplay; // activity is not displayed?
final boolean componentSpecified; // did caller specify an explicit component?
final boolean rootVoiceInteraction; // was this the root activity of a voice interaction?
static final int APPLICATION_ACTIVITY_TYPE = 0;
static final int HOME_ACTIVITY_TYPE = 1;
static final int RECENTS_ACTIVITY_TYPE = 2;
int mActivityType;
CharSequence nonLocalizedLabel; // the label information from the package mgr.
int labelRes; // the label information from the package mgr.
int icon; // resource identifier of activity's icon.
int logo; // resource identifier of activity's logo.
int theme; // resource identifier of activity's theme.
int realTheme; // actual theme resource we will use, never 0.
int windowFlags; // custom window flags for preview window.
TaskRecord task; // the task this is in.
long createTime = System.currentTimeMillis();
long displayStartTime; // when we started launching this activity
long fullyDrawnStartTime; // when we started launching this activity
long startTime; // last time this activity was started
long lastVisibleTime; // last time this activity became visible
long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity
long pauseTime; // last time we started pausing the activity
long launchTickTime; // base time for launch tick messages
Configuration configuration; // configuration activity was last running in
......
可以看到这个类包含了所有的和Activity相关的属性。
而保存Task的List则在ActivityStack.java中:
/**
* The back history of all previous (and possibly still
* running) activities. It contains #TaskRecord objects.
*/
private final ArrayList mTaskHistory = new ArrayList<>();
而这个类中还有更多的变量:
......
/**
* When we are in the process of pausing an activity, before starting the
* next one, this variable holds the activity that is currently being paused.
*/
ActivityRecord mPausingActivity = null;
/**
* This is the last activity that we put into the paused state. This is
* used to determine if we need to do an activity transition while sleeping,
* when we normally hold the top activity paused.
*/
ActivityRecord mLastPausedActivity = null;
/**
* Activities that specify No History must be removed once the user navigates away from them.
* If the device goes to sleep with such an activity in the paused state then we save it here
* and finish it later if another activity replaces it on wakeup.
*/
ActivityRecord mLastNoHistoryActivity = null;
/**
* Current activity that is resumed, or null if there is none.
*/
ActivityRecord mResumedActivity = null;
/**
* This is the last activity that has been started. It is only used to
* identify when multiple activities are started at once so that the user
* can be warned they may not be in the activity they think they are.
*/
ActivityRecord mLastStartedActivity = null;
......
这些则是在进行Activity切换时系统需要用到的信息,比如mPausingActivity,我们知道Activity启动的时候,会等到前一个Activity的onPause完成之后才会正式启动Activity,这个变量就是保存这个的。(所以不能在onPause中进行耗时操作,这会导致下一个Activity启动缓慢。)
Model层我们都在framework中找到了对应的实现,那么Controller是谁呢?答案是ActivityStackSupervisor.java这个类,在最新的Android 7.X的版本中,Google做了代码重构,将Activity启动的一部分逻辑抽出来,形成了ActivityStarter.java,这个类也算是一个Controller。
比如我们一开始提到的launchMode的处理:
// setTaskFromInTask
......
// Check whether we should actually launch the new activity in to the task,
// or just reuse the current activity on top.
ActivityRecord top = mInTask.getTopActivity();
if (top != null && top.realActivity.equals(mStartActivity.realActivity) && top.userId == mStartActivity.userId) {
if ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
|| mLaunchSingleTop || mLaunchSingleTask) {
ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.task);
if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
// We don't need to start a new activity, and the client said not to do
// anything if that is the case, so this is it!
return START_RETURN_INTENT_TO_CALLER;
}
top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
return START_DELIVERED_TO_TOP;
}
}
if (!mAddingToTask) {
// We don't actually want to have this activity added to the task, so just
// stop here but still tell the caller that we consumed the intent.
ActivityOptions.abort(mOptions);
return START_TASK_TO_FRONT;
}
mStartActivity.setTask(mInTask, null);
if (DEBUG_TASKS) Slog.v(TAG_TASKS,
"Starting new activity " + mStartActivity + " in explicit task " + mStartActivity.task);
......
我觉得,阅读源代码的正确姿势应该是先想想如果自己来写,应该怎么设计,然后再看源码是怎么设计,怎么写的,这样才能学习提高自己程序设计的能力,而不是直接就开始读,这样只会迷失在茫茫的代码海洋中。