TaskRecord、ActivityRecord 和 ActivityStack的关系

我们从一个最常见的面试题开始:描述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);
......

我觉得,阅读源代码的正确姿势应该是先想想如果自己来写,应该怎么设计,然后再看源码是怎么设计,怎么写的,这样才能学习提高自己程序设计的能力,而不是直接就开始读,这样只会迷失在茫茫的代码海洋中。

你可能感兴趣的:(Android)