The default mode of launching an Activity changes with the Intent Flag setting. Therefore, the kan Activity startup mode mostly follows the process and pretends to be tough. At most, it is difficult to make difficulties for the interviewer based on the problems encountered in the project. Maybe in the end, both the interviewer and the interviewer will give wrong answers. For example, in a Service, FLAG_ACTIVITY_NEW_TASK must be set to start the Activity. What happens if you start Activit? The answer to this one question has several scenarios:

  • Whether the Task stack for the Activity’s taskAffinity attribute exists
  • If so, check to see if the Activity already exists in the Task
  • If the Task already exists in the taskAffinity, check whether it is its rootActivity
  • In the case of rootActivity, the Intent that started the Activity is equal to the current Intent

Different scenarios, the behavior of the performance will be different, then singleInstance attributes, for example, if set, as we all know, there is only one instance can reuse again in the future, but if use Intent. FLAG_ACTIVITY_CLEAR_TASK to start, will still be rebuilt, The instructions of singleInstance are not fully followed, and different flags may behave differently when used superimposed. It is difficult to start an Activity in a single mode. This article is only involved in part of the startup mode and Flag, more combination with the scene to see their own source code or experiment to solve.

Simple basic LaunchMode

This article assumes that you have a basic understanding of the Activity’s Task stack. First, let’s take a look at the four common startup modes of an Activity.

  • Standard: The standard startup mode (the default), which starts a new activity instance each time.
  • With this pattern, if the Activity instance is at the top of the current task stack, it is reused instead of being created, and the onNewIntent() method is called back, otherwise it goes through the new process.
  • SingleTask: In this mode, only one instance of an Activity exists in the taskAffinit Task stack at a time. When an existing instance is started again, it is recalled and all activities on that instance in the current Task stack are cleared. The onNewIntent() method is also called back.
  • SingleInstance: An Activity launched in this mode occupies a single Task stack, and only one instance exists in the system at a time. When an existing instance is started again, only the original instance is invoked and the onNewIntent() method is called back.

To be clear: Note that FLAG_ACTIVITY_NEW_TASK is used only when an Activity starts an Activity with a default Intent and does not add any additional flags. FLAG_ACTIVITY_NEW_TASK actually falls into two groups.

Intent. FLAG_ACTIVITY_NEW_TASK analysis

Intent.flag_activity_new_task is the most important Flag in the intent.flag_activity_new_task mode. Intent.flag_activity_new_task is displayed for activities that are not launched as activities (such as those launched in a Service). SingleTask and singleInstance are preprocessed in AMS. Intent.FLAG_ACTIVITY_NEW_TASK is set implicitly, while standard and singletTop activities are not set to intent. FLAG_ACTIVITY_NEW_TASK. Unless the intent setFlag is displayed.

FLAG_ACTIVITY_NEW_TASK focuses more on tasks. In most cases, you need to introduce activities into your taskAffinity Task. Intent.flag_activity_new_task is intended to be launched in the Activity target Task taskAffinity. FLAG_ACTIVITY_NEW_TASK is an Intent that must be added to any Activity that is not an Activity.

  Intent intent = new Intent(BackGroundService.this, A.class);
  intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  startActivity(intent);    Copy the code

This is an interesting situation. If the target Activity instance or Task does not exist, a new Activity is created and the target Task is brought to the foreground, but if the Activity exists, it is not necessarily reused or visible. If there is already an instance of A, and the stack’s taskAffinity matches that of A’s taskAffinity, then the task’s root Activity is A. If it is A, the task’s root Activity is A. If the intent of A is equal to the intent of the current launch, the task should be visible. Otherwise, you need to create A and choose whether to directly stack or create A new stack depending on the existence of A’s task stack. However, if the target stack of the Activity that the Intent wants to start exists, the entire stack is moved forward. If the Task stack at the top happens to be the target Activity’s Task stack, nothing is done, not even the onNewIntent callback is done. How to determine if the target Activity’s Task stack matches the found stack? If the target Task is not found, the Task is automatically started. If the intent of the target Task Activit is the same as that of the new Activit to be started, the new Activity is not started; otherwise, the Activity is started.

The Service through FLAG_ACTIVITY_NEW_TASK. JPG

Intent.flag_activity_clear_task: must be used with 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.

This attribute must be used together with FLAG_ACTIVITY_NEW_TASK, if set FLAG_ACTIVITY_NEW_TASK | Intent. FLAG_ACTIVITY_CLEAR_TASK, if the target task already exists. Empty existing target tasks, otherwise create a new Task stack, and then create a new Activity as the root Activity. Intent.FLAG_ACTIVITY_CLEAR_TASK is the highest priority in the intent. FLAG_ACTIVITY_CLEAR_TASK is the highest priority in the intent. FLAG_ACTIVITY_CLEAR_TASK is the highest priority.

Intent.FLAG_ACTIVITY_CLEAR_TASK.jpg

Intent.FLAG_ACTIVITY_CLEAR_TOP

FLAG_ACTIVITY_CLEAR_TOP: intent.activitY_clear_top: intent.activity_clear_top: intent.activity_clear_top: intent.activity_clear_top: intent.activity_clear_top: intent.activity_clear_top: intent.activity_clear_top Google’s official example is: FLAG_ACTIVITY_CLEAR_TOP: intent.flag_activity_clear_top: intent.activity_clear_top: intent.activity_clear_top: intent.activity_clear_top: intent.activity_clear_top: intent.activity_clear_top: intent.activity_clear_top: intent.activity_clear_top: intent.activity_clear_top B finish is dropped and a new push is created. If you had one in the same stack

Intent.flag_activity_clear_top Same.jpg

If not, create a new stack without looking in another stack.

Intent.FLAG_ACTIVITY_CLEAR_TOP.jpg

If FLAG_ACTIVITY_SINGLE_TOP is set at the same time, the stack will not rebuild if it is already in the stack, and instead calls onNewIntent() of B directly,

Intent.FLAG_ACTIVITY_CLEAR_TOP|SINGLE_TOP.jpg

Here’s the official explanation:

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().

When FLAG_ACTIVITY_NEW_TASK is used, the target is the Activity’s own Task stack. If an Activity instance can be found in your Task, clear it and itself, and then rebuild.

Intent.FLAG_ACTIVITY_CLEAR_TOP| FLAG_ACTIVITY_NEW_TASK.jpg

FLAG_ACTIVITY_SINGLE_TOP is even more special. If topActivity is not the target Activity, it will be found in the target Task and raised

Intent.FLAG_ACTIVITY_CLEAR_TOP| FLAG_ACTIVITY_NEW_TASK|singleTop.jpg

If topActivity is the target Activity, call back topActivity’s onNewIntent directly, regardless of whether topActivity is in the target Task or not

Intent.FLAG_ACTIVITY_CLEAR_TOP| FLAG_ACTIVITY_NEW_TASK|singleTop|top.jpg

Intent.FLAG_ACTIVITY_SINGLE_TOP

Intent.flag_activity_single_top: intent.flag_activity_single_top: intent.flag_activity_single_top: intent.flag_activity_single_top FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_CLEAR_TOP have interesting effects.

Source code analysis

FLAG_ACTIVITY_NEW_TASK is more important than FLAG_ACTIVITY_NEW_TASK in the source code, but this is only part of the scenario, otherwise there will be too many:

final int startActivityUncheckedLocked(ActivityRecord r,
        ActivityRecord sourceRecord, int startFlags, boolean doResume, Bundle options) { final Intent intent = r.intent; final int callingUid = r.launchedFromUid; . <! Int launchFlags = intent.getFlags(); . <! FLAG_ACTIVITY_NEW_TASK--> // IfsourceRecord ==null indicates that the service is not started from the activity and must start a new task from the serviceif (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; }... <! -- Keypoint 3 for intent.flag_activity_new_task pairssourceThe Activity returns 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; <! 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) { <! ActivityRecord taskTop = r.launchMode! = ActivityInfo.LAUNCH_SINGLE_INSTANCE ? findTaskLocked(intent, r.info) : findActivityLocked(intent, r.info); <! -- If target stack exists -->if(taskTop ! = null) { ActivityRecord curTop = topRunningNonDelayedActivityLocked(notTop); <! -- Keypoint 6 to see if the found Task is at the top of the stack, if not move -->if(curTop ! = null && curTop.task ! = taskTop.task) { r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); booleancallerAtFront = sourceRecord == null
                            || curTop.task == sourceRecord.task;
                    if (callerAtFront) {
                        movedHome = true; moveHomeToFrontFromLaunchLocked(launchFlags); moveTaskToFrontLocked(taskTop.task, r, options); options = null; }}... <! FLAG_ACTIVITY_CLEAR_TASK if intent.flag_activity_clear_task is set, the Task is reset and reuseTask is assigned. This property can only be used with 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); <! FLAG_ACTIVITY_CLEAR_TOP: intent.flag_activity_clear_top: intent.flag_activity_clear_topelse if((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) ! = 0 || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { <! ActivityRecord Top = performClearTaskLocked(taskTop.task.taskid, r, launchFlags);if(top ! = null) { <! Reset the intent if it is rootActivity -->if (top.frontOfTask) {
                            top.task.setIntent(r.intent, r.info);
                        }
                        top.deliverNewIntentLocked(callingUid, r.intent);
                    } else{<! If it can't find it, create a new onesourceRecord assignment --> addingToTask =true;
                        sourceRecord = taskTop; }}else if(r.realActivity.equals(taskTop.task.realActivity)) { <! FLAG_ACTIVITY_SINGLE_TOP: intent.flag_actiVITY_singLE_TOP: intent.flag_actiVITY_singLE_top: intent.flag_actiVITY_singLE_top: intent.flag_actiVITY_singLE_top: intent.flag_actiVITy_single_topif(((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); <! If the root intent is not equal, create a new Activity and add it to the target stack.else if(! r.intent.filterEquals(taskTop.task.intent)) { addingToTask =true;
                        sourceRecord = taskTop; }}... <! Intent.flag_activity_new_task (intent.flag_activity_new_task)if(! addingToTask && reuseTask == null) {if (doResume) {
                        resumeTopActivityLocked(null, options);
                    } else {
                        ActivityOptions.abort(options);
                    }
                    returnActivityManager.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) { <! FLAG_ACTIVITY_SINGLE_TOP--> 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);
                        returnActivityManager.START_DELIVERED_TO_TOP; }}}}}... boolean newTask =false;
    boolean keepCurTransition = false; <! Finally decide whether to start the new Task stack.if(r.resultTo == null && ! addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) ! {< = 0). FLAG_ACTIVITY_NEW_TASK--> reuseTask Task not found in launchFlags&Intent -->if (reuseTask == null) {
            mService.mCurTask++;
            if (mService.mCurTask <= 0) {
                mService.mCurTask = 1;
            }
            r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true);
        } else{<! --> r.setTask(reuseTask, reuseTask,true);
        }
        newTask = true;
        if (!movedHome) {
            moveHomeToFrontFromLaunchLocked(launchFlags);
        }

    } else if (sourceRecord ! = null) { <! If the current stack can find the singleTop of the target stack, do not create a new Activity, otherwise create a new -->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);
                returnActivityManager.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;
}Copy the code

Why is FLAG_ACTIVITY_NEW_TASK mandatory for non-activity startup activities

Says from the source, ContextImpl did check in the early, if not add Intent. FLAG_ACTIVITY_NEW_TASK exception is thrown,

@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?"); }... }Copy the code

Why do we do that? FLAG_ACTIVITY_NEW_TASK (intEnt.flag_activity_new_task, intent.intent.flag_activity_new_task, intent.intent.activity_task, intent.intent.flag_activity_new_task) Then the user might think that the Activity is the page of the currently visible APP, which is not reasonable. Here’s an example: If the mail Service at this time we are listening to music, suddenly want to open an Activity, if not the Intent, FLAG_ACTIVITY_NEW_TASK do limit, the user might think this Activity is belong to the music APP, because when users click on the return, Might go back to music instead of mail (if mail had an interface before).

conclusion

The above analysis is only for one version of Android, and only involves part of the Flag, it is more difficult to fully understand the various combinations. Therefore, if the interviewer questions the Activity startup mode, it is ok to talk freely, but to despise you, there is a 90% chance that you can fight back. Put a few flags together and ask the interviewer what the result will be.

Let you pack to force

The Android interviewer installs the failure: The Activity starts in a mode for reference only, welcome to correct