Android面试官装逼失败之:Activity的启动模式

面试的时候,面试官常常同你随便侃侃Activity的启动模式,但Activity启动牵扯的知识点其实不少,并不是能单单用四个启动模式就能归纳的,默认的启动模式的表现会随着Intent Flag的设置而改变,所以侃Activity启动模式大多走流程装逼,最多结合项目遇到的问题,随便刁难一下面试者,并不太容易把控,也许最后,面试官跟面试者的答案都是错了,好比在Service中必须经过设置FLAG_ACTIVITY_NEW_TASK才能启动Activity,这个时候启动Activit会有什么样的表现呢?就这一个问题,答案就要分好几个场景:面试

  • Activity的taskAffinity属性的Task栈是否存在
  • 若是存在,要看Activity是否存已经存在于该Task
  • 若是已经存在于该taskAffinity的Task,要看其是否是其rootActivity
  • 若是是其rootActivity,还要看启动该Activity的Intent是否跟当前intent相等

不一样场景,所表现的行为都会有所不一样,再好比singleInstance属性,若是设置了,你们都知道只有一个实例,未来再启动会复用,可是若是使用Intent.FLAG_ACTIVITY_CLEAR_TASK来启动,仍然会重建,并不是彻底遵照singleInstance的说明,还有不一样Flag在叠加使用时候也会有不一样的表现,单一而论Activity启动模式实际上是很难的。本文也仅仅是涉及部分启动模式及Flag,更多组合跟场景要本身看源码或者实验来解决了。bash

简单基本launchmode

本文假定你们对于Activity的Task栈已经有初步了解,首先,看一下Activity常见的四种启动模式及大众理解,这也是面试时最长问的:app

  • standard:标准启动模式(默认启动模式),每次都会启动一个新的activity实例。
  • singleTop:单独使用使用这种模式时,若是Activity实例位于当前任务栈顶,就重用栈顶实例,而不新建,并回调该实例onNewIntent()方法,不然走新建流程。
  • singleTask:这种模式启动的Activity只会存在相应的Activity的taskAffinit任务栈中,同一时刻系统中只会存在一个实例,已存在的实例被再次启动时,会从新唤起该实例,并清理当前Task任务栈该实例之上的全部Activity,同时回调onNewIntent()方法。
  • singleInstance:这种模式启动的Activity独自占用一个Task任务栈,同一时刻系统中只会存在一个实例,已存在的实例被再次启动时,只会唤起原实例,并回调onNewIntent()方法。

须要说明的是:上面的场景仅仅适用于Activity启动Activity,而且采用的都是默认Intent,没有额外添加任何Flag,不然表现就可能跟上面的彻底不一致,尤为要注意的是FLAG_ACTIVITY_NEW_TASK的使用,后面从源码中看,依靠FLAG_ACTIVITY_NEW_TASK其实能够分为两派。ide

Intent.FLAG_ACTIVITY_NEW_TASK分析

从源码来看,Intent.FLAG_ACTIVITY_NEW_TASK是启动模式中最关键的一个Flag,依据该Flag启动模式能够分红两类,设置了该属性的与未设置该属性的,对于非Activity启动的Activity(好比Service中启动的Activity)须要显示的设置Intent.FLAG_ACTIVITY_NEW_TASK,而singleTask及singleInstance在AMS中被预处理后,隐形的设置了Intent.FLAG_ACTIVITY_NEW_TASK,而启动模式是standard及singletTop的Activity不会被设置Intent.FLAG_ACTIVITY_NEW_TASK,除非经过显示的intent setFlag进行设置。源码分析

FLAG_ACTIVITY_NEW_TASK这个属性更多的关注点是在Task,大多数状况下,须要将Activity引入到本身taskAffinity的Task中,Intent.FLAG_ACTIVITY_NEW_TASK的初衷是在Activity目标taskAffinity的Task中启动,非Activity启动Activity都必须添加Intent.FLAG_ACTIVITY_NEW_TASK才行,以Service启动的Activity为例:ui

Intent intent = new Intent(BackGroundService.this, A.class);
  intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  startActivity(intent);    复制代码

这种状况颇有意思,若是目标Activity实例或者Task不存在,则必定会新建Activity,并将目标Task移动到前台,可是若是Activity存在,却并不必定复用,也不必定可见。这里假定A是standard的Activity,若是已经有一个A实例,而且所在的堆栈的taskAffinity跟A的taskAffinity一致,这个时候要看这个task的根Activity是否是A,若是是A,还要看A的intent是否是跟当前的启动的intent相等,若是都知足,只要将task可见便可。不然,就须要新建A,并根据A的task栈的存在状况而选择直接入栈仍是新建栈。可是,若是Intent想要的启动的Activity的目标堆栈存在,那就将整个堆栈往前迁移,若是位于顶部的Task栈正好是目标Activity的Task栈,那就不作任何处理,连onNewIntent都不会回调,怎么判断目标的Activity的Task栈同找到的栈一致呢?若是找不到目标Task天然会启动Task,若是目标task栈根Activit的intent同新将要启动的Activit相同,就不启动新Activity,不然启动Activitythis

Service 经过 FLAG_ACTIVITY_NEW_TASK.jpg
Service 经过 FLAG_ACTIVITY_NEW_TASK.jpg

Intent.FLAG_ACTIVITY_CLEAR_TASK:必须配合FLAG_ACTIVITY_NEW_TASK使用

If set in an Intent passed to Context.startActivity(), this flag will cause any existing task that would be associated with the activity to be cleared before the activity is started. That is, the activity becomes the new root of an otherwise empty task, and any old activities are finished. This can only be used in conjunction with FLAG_ACTIVITY_NEW_TASK.spa

这个属性必须同FLAG_ACTIVITY_NEW_TASK配合使用,若是设置了FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK,若是目标task已经存在,将清空已存在的目标Task,不然,新建一个Task栈,以后,新建一个Activity做为根Activity。Intent.FLAG_ACTIVITY_CLEAR_TASK的优先级最高,基本能够无视全部的配置,包括启动模式及Intent Flag,哪怕是singleInstance也会被finish,并重建。3d

Intent.FLAG_ACTIVITY_CLEAR_TASK.jpg
Intent.FLAG_ACTIVITY_CLEAR_TASK.jpg

Intent.FLAG_ACTIVITY_CLEAR_TOP

若是没有使用FLAG_ACTIVITY_NEW_TASK,目标是当前Task栈,根据不一样的组合会产生不一样的效果,若是单独使用Intent.FLAG_ACTIVITY_CLEAR_TOP,而且没有设置特殊的launchmode,那么,Google官方的示例是:若是ABCD Task中的D采用Intent.FLAG_ACTIVITY_CLEAR_TOP唤起B,这个时候首先会将CD出栈,可是至于B是否会重建,要视状况而定,若是没有设置FLAG_ACTIVITY_SINGLE_TOP,则会将B finish掉,以后建立新的入栈。若是同一个栈中原来有rest

Intent.FLAG_ACTIVITY_CLEAR_TOP同一个.jpg
Intent.FLAG_ACTIVITY_CLEAR_TOP同一个.jpg

若是没有则新建,不会去另外一个栈中寻找。

Intent.FLAG_ACTIVITY_CLEAR_TOP.jpg
Intent.FLAG_ACTIVITY_CLEAR_TOP.jpg

若是同时设置了FLAG_ACTIVITY_SINGLE_TOP,在当前栈已有的状况下就不会重建,而是直接回调B的onNewIntent(),

Intent.FLAG_ACTIVITY_CLEAR_TOP|SINGLE_TOP.jpg
Intent.FLAG_ACTIVITY_CLEAR_TOP|SINGLE_TOP.jpg

官方解释以下:

For example, consider a task consisting of the activities: A, B, C, D. If D calls startActivity() with an Intent that resolves to the component of activity B, then C and D will be finished and B receive the given Intent, resulting in the stack now being: A, B。

The currently running instance of activity B in the above example will either receive the new intent you are starting here in its onNewIntent() method, or be itself finished and restarted with the new intent. If it has declared its launch mode to be "multiple" (the default) and you have not set FLAG_ACTIVITY_SINGLE_TOP in the same intent, then it will be finished and re-created; for all other launch modes or if FLAG_ACTIVITY_SINGLE_TOP is set then this Intent will be delivered to the current instance's onNewIntent().

若是同时使用了FLAG_ACTIVITY_NEW_TASK ,这个时候,目标是Activity本身所属的Task栈,若是在本身的Task中能找到一个Activity实例,则将其上面的及自身清理掉,以后重建。

Intent.FLAG_ACTIVITY_CLEAR_TOP| FLAG_ACTIVITY_NEW_TASK.jpg
Intent.FLAG_ACTIVITY_CLEAR_TOP| FLAG_ACTIVITY_NEW_TASK.jpg

若是同时在加上FLAG_ACTIVITY_SINGLE_TOP,会更特殊一些,若是topActivity不是目标Activity,就会去目标Task中去找,并唤起

Intent.FLAG_ACTIVITY_CLEAR_TOP| FLAG_ACTIVITY_NEW_TASK|singleTop.jpg
Intent.FLAG_ACTIVITY_CLEAR_TOP| FLAG_ACTIVITY_NEW_TASK|singleTop.jpg

若是topActivity是目标Activity,就直接回调topActivity的onNewIntent,不管topActivity是否是在目标Task中

Intent.FLAG_ACTIVITY_CLEAR_TOP| FLAG_ACTIVITY_NEW_TASK|singleTop|top.jpg
Intent.FLAG_ACTIVITY_CLEAR_TOP| FLAG_ACTIVITY_NEW_TASK|singleTop|top.jpg

Intent.FLAG_ACTIVITY_SINGLE_TOP

Intent.FLAG_ACTIVITY_SINGLE_TOP多用来作辅助做用,跟launchmode中的singleTop做用同样,在Task栈顶有的话,就不新建,栈顶没有的话,就新建,这里的Task多是目标栈,也多是当前Task栈,配合FLAG_ACTIVITY_NEW_TASK及FLAG_ACTIVITY_CLEAR_TOP都会有颇有意思的效果。

源码分析

如今咱们看一下源码,来分析下原理:从源码更能看出FLAG_ACTIVITY_NEW_TASK重要性,这里只分析部分场景,否则太多了: 下面的是4.3的代码,比较简单些:

final int startActivityUncheckedLocked(ActivityRecord r,
        ActivityRecord sourceRecord, int startFlags, boolean doResume,
        Bundle options) {
    final Intent intent = r.intent;
    final int callingUid = r.launchedFromUid;
    ...
    <!--关键点1:直接获取launchFlags--> 
    int launchFlags = intent.getFlags();
    ...
     <!--关键点2:预处理一些特殊的 launchmode,主要是设置Intent.FLAG_ACTIVITY_NEW_TASK-->
    // 若是sourceRecord ==null 说明不是从activity启动的,从服务开启就必定要start a new task
    if (sourceRecord == null) {
        if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
            launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
        }
    } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
        launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
    } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
        launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
    }
    ...
    <!--关键点3 对于Intent.FLAG_ACTIVITY_NEW_TASK 对sourceActivity直接返回cancel-->
    if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
        sendActivityResultLocked(-1,
                r.resultTo, r.resultWho, r.requestCode,
            Activity.RESULT_CANCELED, null);
        r.resultTo = null;
    }
    boolean addingToTask = false;
    boolean movedHome = false;
    TaskRecord reuseTask = null;
     <!--关键点4 对于Intent.FLAG_ACTIVITY_NEW_TASK分支的处理-->
    if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
            (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
        if (r.resultTo == null) {
            <!--关键点5找到目标Task栈的栈顶元素 ,可是taskTop不必定是目标Activity-->   
            ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
                    ?findTaskLocked(intent, r.info)
                    : findActivityLocked(intent, r.info);
            <!--若是目标栈存在-->
            if (taskTop != null) {
                ActivityRecord curTop = topRunningNonDelayedActivityLocked(notTop);
                <!--关键点6 看找到的Task是否是位于栈顶,若是不是则移动-->
                if (curTop != null && curTop.task != taskTop.task) {
                    r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
                    boolean callerAtFront = sourceRecord == null
                            || curTop.task == sourceRecord.task;
                    if (callerAtFront) {
                        movedHome = true;
                        moveHomeToFrontFromLaunchLocked(launchFlags);
                        moveTaskToFrontLocked(taskTop.task, r, options);
                        options = null;
                    }
                }
                    ...
                    <!--关键点6 若是设置了Intent.FLAG_ACTIVITY_CLEAR_TASK,则重置Task,并为reuseTask赋值,这个属性只能配合Intent.FLAG_ACTIVITY_NEW_TASK使用-->
                if ((launchFlags &
                        (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK))
                        == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK)) {
                    reuseTask = taskTop.task;
                    performClearTaskLocked(taskTop.task.taskId);
                    reuseTask.setIntent(r.intent, r.info);
                 <!--关键点7 若是设置了Intent.FLAG_ACTIVITY_CLEAR_TOP,则将Task中目标Activity之上的清空,至于本身是否要清空,还要看是否是设置了singltTop-->
                } else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
                        || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
                        || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
                    <!--若是能找到一个SingleTop,则不用重建-->
                    ActivityRecord top = performClearTaskLocked(
                            taskTop.task.taskId, r, launchFlags);
                    if (top != null) {
                    <!--若是本身是rootActivity,则重置intent-->
                        if (top.frontOfTask) {
                            top.task.setIntent(r.intent, r.info);
                        }
                        top.deliverNewIntentLocked(callingUid, r.intent);
                    } else {
                     <!--找不到的话,就新建 ,而且为sourceRecord赋值-->
                        addingToTask = true;
                        sourceRecord = taskTop;
                    }
                } else if (r.realActivity.equals(taskTop.task.realActivity)) {
                        <!--若是taskTop的Task rootActivity是目标Activity,则额外,若是是Intent.FLAG_ACTIVITY_SINGLE_TOP,并本身位于栈顶 不新建-->
                    if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP)
                            && taskTop.realActivity.equals(r.realActivity)) {
                        if (taskTop.frontOfTask) {
                            taskTop.task.setIntent(r.intent, r.info);
                        }
                        taskTop.deliverNewIntentLocked(callingUid, r.intent);
                        <!--若是root intent不相等,则新建Activity,并加入目标栈-->
                    } else if (!r.intent.filterEquals(taskTop.task.intent)) {
                        addingToTask = true;
                        sourceRecord = taskTop;
                    }
                }
                 ...
                 <!--以上部分主要是对Intent.FLAG_ACTIVITY_NEW_TASK的处理,有些须要直接返回的场景-->
                if (!addingToTask && reuseTask == null) {
                    if (doResume) {
                        resumeTopActivityLocked(null, options);
                    } else {
                        ActivityOptions.abort(options);
                    }
                    return ActivityManager.START_TASK_TO_FRONT;
                }
            }
        }
    }

    if (r.packageName != null) {
        ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
        if (top != null && r.resultTo == null) {
            if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
                if (top.app != null && top.app.thread != null) {
               <!--可能不是目标task,可是是Intent.FLAG_ACTIVITY_SINGLE_TOP-->
                    if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
                        || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
                        || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
                        logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
                        if (doResume) {
                            resumeTopActivityLocked(null);
                        }
                        ActivityOptions.abort(options);
                        if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
                            return ActivityManager.START_RETURN_INTENT_TO_CALLER;
                        }
                        top.deliverNewIntentLocked(callingUid, r.intent);
                        return ActivityManager.START_DELIVERED_TO_TOP;
                    }
                }
            }
        }

    }
    ...

    boolean newTask = false;
    boolean keepCurTransition = false;

    <!--最终判断是否是要启动新Task栈-->
    if (r.resultTo == null && !addingToTask
            && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
        <!--新建Task 没找到reuseTask task 而且是launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK-->
        if (reuseTask == null) {
            mService.mCurTask++;
            if (mService.mCurTask <= 0) {
                mService.mCurTask = 1;
            }
            r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true);
        } else {
        <!--找到Task,可是rootActivity不是目标Activiyt,则须要新建activity-->
            r.setTask(reuseTask, reuseTask, true);
        }
        newTask = true;
        if (!movedHome) {
            moveHomeToFrontFromLaunchLocked(launchFlags);
        }

    } else if (sourceRecord != null) {
            <!--非服务类启动的Activity,若是当前栈能找到目标栈的singleTop,则不新建,不然新建-->
        if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
            ActivityRecord top = performClearTaskLocked(
                    sourceRecord.task.taskId, r, launchFlags);
            keepCurTransition = true;
            if (top != null) {
                top.deliverNewIntentLocked(callingUid, r.intent);
                if (doResume) {
                    resumeTopActivityLocked(null);
                }
                ActivityOptions.abort(options);
                return ActivityManager.START_DELIVERED_TO_TOP;
            }
        } 
        ...
        r.setTask(sourceRecord.task, sourceRecord.thumbHolder, false);
    } else {
        final int N = mHistory.size();
        ActivityRecord prev =
            N > 0 ? mHistory.get(N-1) : null;
        r.setTask(prev != null
                ? prev.task
                : new TaskRecord(mService.mCurTask, r.info, intent), null, true);
   }
    startActivityLocked(r, newTask, doResume, keepCurTransition, options);
    return ActivityManager.START_SUCCESS;
}复制代码

为何非Activity启动Activity要强制规定使用参数FLAG_ACTIVITY_NEW_TASK

从源码上说,ContextImpl在前期作了检查,若是没添加Intent.FLAG_ACTIVITY_NEW_TASK就抛出异常,

@Override
public void startActivity(Intent intent, Bundle options) {
    warnIfCallingFromSystemProcess();
    if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
        throw new AndroidRuntimeException(
                "Calling startActivity() from outside of an Activity "
                + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                + " Is this really what you want?");
    }
    ...
}复制代码

为何要这么呢?其实直观很好理解,若是不是在Activity中启动的,那就能够看作不是用户主动的行为,也就说这个界面可能出如今任何APP之上,若是不用Intent.FLAG_ACTIVITY_NEW_TASK将其限制在本身的Task中,那用户可能会认为该Activity是当前可见APP的页面,这是不合理的。举个例子:咱们在听音乐,这个时候若是邮件Service忽然要打开一个Activity,若是不用Intent.FLAG_ACTIVITY_NEW_TASK作限制,那用户可能认为这个Activity是属于音乐APP的,由于用户点击返回的时候,可能会回到音乐,而不是邮件(若是邮件以前就有界面)。

总结

以上分析只是针对一个版本的Android,而且只涉及部分Flag,要彻底理解各类组合就更麻烦了,因此所,若是面试官问题Activity启动模式的话,随便侃侃还能够,可是要以此来鄙视你,那你有90%的几率能够怼回去,怎么怼?随便几个Flag组合一下,问面试官会有什么结果,保证问死一堆 ^_^;

让你装逼
让你装逼

做者:看书的小蜗牛
原文连接: Android面试官装逼失败之:Activity的启动模式
仅供参考,欢迎指正

相关文章
相关标签/搜索