写在前面:Android APP有四种启动模式——》标准模式(Standard)、栈顶复用模式(SingleTop)、栈内复用模式(SingleTask)、单例模式(SingleInstance),默认就是标准模式。启动模式决定了Activity在任务栈内的存在方式,影响了Back返回键Activity返回的顺序。启动模式看起来简单,但是实际上想要控制某些Activity的返回顺序,需要结合两个部分一起实现——》一是在AndroidManifest.xml中合理指定Activity的启动模式;二是在使用Intent启动Activity时传入合理的启动Flag。好,讲了这么多其实就是想说APP启动模式最大的作用是用来控制Activity的返回顺序,它由两部分共同进行控制,控制起来比较复杂,有必要好好学一学,把它搞清楚。
开始之前我们先来明确一下概念,就是什么是任务栈:
任务栈:一个APP的所有Activity实例是用栈来进行管理的,当我们启动一个Activity,这个Activity就会入栈处在栈顶,只有在栈顶的Activity是用户当前可见的,按Back键栈顶的Activity就会出栈,如果栈里面还有Activity就继续显示栈顶Activity,没有就直接退出APP,这就是为什么打开的Activity多了需要按多次Back键才能退出的原因。
注:1、Home键不会导致出栈操作,这也是为什么使用Recent键打开最近使用APP能接着之前的Activity页面使用的原因。2、系统中存在多个任务栈,每个APP存在至少一个任务栈,不同的APP任务栈肯定不一样。
下面从四种启动模式开始讲解。
一、标准模式(Standard)
没在AndroidManifest.xml指定Activity的启动模式,模式就是标准模式。Standard 是 Android Activity 的默认启动模式,每次启动 Activity 时,都会创建一个新的实例,并放入当前任务栈(Task)的顶部。即使该 Activity 已经在任务栈中,也会创建一个新的实例,而不会复用已有实例。
下面画张图举个例子看一下:同一个栈里有Activity1、Activity2,多次启动Activity1。
可以看到每次启动Actvity1都会重新创建,没有复用之前的栈内实例。真实的栈可以使用如下adb命令查看:执行命令打印会很多,关注Task display areas in top down Z order:的相关字段即可,比如Task{ec5b36c就是一个已经存在的任务栈。
adb命令:dumpsys activity activitiesTask display areas in top down Z order:TaskDisplayArea DefaultTaskDisplayAreamPreferredTopFocusableRootTask=Task{ec5b36c #115 type=standard A=10158:com.htc.app_launch_mode U=0 visible=true mode=fullscreen translucent=false sz=2}mLastFocusedRootTask=Task{ec5b36c #115 type=standard A=10158:com.htc.app_launch_mode U=0 visible=true mode=fullscreen translucent=false sz=2}Application tokens in top down Z order:* Task{ec5b36c #115 type=standard A=10158:com.htc.app_launch_mode U=0 visible=true mode=fullscreen translucent=false sz=2}bounds=[0,0][1440,3120]* ActivityRecord{ed84d3d u0 com.htc.app_launch_mode/.MainActivity t115}* ActivityRecord{61c5b7d u0 com.htc.app_launch_mode/.MainActivity t115}* Task{6938da2 #1 type=home ?? U=0 visible=false mode=fullscreen translucent=true sz=1}bounds=[0,0][1440,3120]* Task{d8594e9 #112 type=home I=com.google.android.apps.nexuslauncher/.NexusLauncherActivity U=0 rootTaskId=1 visible=false mode=fullscreen translucent=true sz=1}bounds=[0,0][1440,3120]* ActivityRecord{92f632c u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity t112}
二、栈内复用模式(SingleTask)
SingleTask 启动模式意味着:在任务栈(Task)中只会存在一个该 Activity 的实例。如果 Activity 已经存在于任务栈中,则不会重新创建,而是复用已有实例,并调用它的 onNewIntent() 方法。复用时,该 Activity 之上的所有 Activity 都会被清除(类似“回退”操作)。
使用方式直接在AndroidManifest.xml中显示指定launchMode即可,如下所示:
<activityandroid:name=".Activity2"android:exported="false"android:launchMode="singleTask"/><activityandroid:name=".MainActivity"android:exported="true"android:launchMode="singleTask"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity>
假设现在有5个Activity,Activity1、Activity3是栈内复用模式,用下面的图进行说明:
三、栈顶复用模式(SingleTop)
这种模式的特点是——》如果 Activity 已经在栈顶,则不会新建实例,而是复用当前实例并调用 onNewIntent() 方法。如果 Activity 不在栈顶,则仍会创建新实例。不会清除任务栈中的其他 Activity,与 singleTask 不同。可以用下面这张图来做一个形象的表示:假设有两个Activity,Activity1、Activity2都是栈顶模式。
可以看到只有在栈顶的会被复用。
四、单例模式(SingleInstance)
单例模式的特点——》Activity将独占一个任务栈,且APP所有的任务栈中将只存在一个Activity实例对象。
用下面这张图举个例子:假设有三个Activity,Activity1、Activity2是非单例模式,Activity3是单例模式。
通过上图可以看到,我们从Activity1开始,但是最终返回的最后一个界面却变成了Activity3,Activity3再返回就是桌面了,这就是不同的启动模式对Activity返回顺序的影响,如果业务复杂,Activity很多,四种模式混在一起用,要准确控制Activity的返回顺序就是一件比较困难的事情了。
五、Intent启动Activity附带的Flag
5.1 FLAG_ACTIVITY_NEW_TASK
这个标志位应该是大家最熟悉的了,如下使用:
Intent intent = new Intent(this, TargetActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
它最大的特点在于:当 Activity 不是从另一个 Activity 中启动,而是从 Service、BroadcastReceiver 或 Application 启动时,必须使用 FLAG_ACTIVITY_NEW_TASK,否则会导致报错 android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag.因为Service、BroadcastReceiver 或 Application本身是不在任务栈中运行的,不存在任务栈,必须加上FLAG_ACTIVITY_NEW_TASK去新建任务栈。当然了如果APP已经存在了任务栈,那么就不会去创建新的任务栈,而是根据Activity的启动模式合理复用或者入栈。
注意:FLAG_ACTIVITY_NEW_TASK是APP没有任务栈的时候去新建,有了的话就不会。
所以启动Activity都会加上这个Flag,不会有什么副作用,还能防止出错,属于万金油了。示意图如下:
5.2 FLAG_ACTIVITY_CLEAR_TASK
这个FLAG的意思就是在启动Activity之前,清空当前启动它的任务栈,Standard、SingleTask、SingleTop三种模式完全服从FLAG_ACTIVITY_CLEAR_TASK,但是SingleInstance 却不是这样,它会影响这个FLAG的行为,具体来说分两种:1、SingleInstance的Activity启动其它模式的Activity。2、其它模式的Activity启动SingleInstance的Activity。使用起来也很简单:
Intent intent = new Intent(this, TargetActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
下面画一张图来做个演示:三个Activity,Activity1、Activity3非单例模式,Activity2单例模式。
可以看到,Activity1启动Activity2对应的是非单例模式Activity启动单例模式Activity:已有任务栈Task1不会被清空,而是新建一个任务栈Task2启动单例Activity。Activity2启动Activity3对应单例模式Activity启动非单例模式Activity:Task2没有任何影响,Task1中的Activity1被清除,取而代之的是Activity3。
5.3 FLAG_ACTIVITY_MATCH_EXTERNAL
所有的FLAG没有写完,后续会陆续补充完成。
5.4 FLAG_ACTIVITY_SINGLE_TOP
5.5 FLAG_ACTIVITY_MULTIPLE_TASK
5.6 FLAG_ACTIVITY_FORWARD_RESULT
5.7 FLAG_ACTIVITY_FORWARD_RESULT
5.8 FLAG_ACTIVITY_PREVIOUS_IS_TOP
5.9 FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
5.10 FLAG_ACTIVITY_BROUGHT_TO_FRONT
5.11 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
5.12 FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
5.13 FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
5.14 FLAG_ACTIVITY_NEW_DOCUMENT
5.15 FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
5.16 FLAG_ACTIVITY_NO_USER_ACTION
5.17 FLAG_ACTIVITY_REORDER_TO_FRONT
5.18 FLAG_ACTIVITY_NO_ANIMATION
5.19 FLAG_ACTIVITY_TASK_ON_HOME
5.20 FLAG_ACTIVITY_RETAIN_IN_RECENTS
5.21 FLAG_ACTIVITY_LAUNCH_ADJACENT