在开始之前,我们首先需要了解一下Android中任务和返回栈(back stack).
任务是指在执行特定作业时与用户交互的一系列 Activity。 这些 Activity 按照各自的打开顺序排列在堆栈(即返回栈)中。
也可以说一个任务就是一组放在栈里的活动的集合,而存储任务的栈我们称之为返回栈。
对于栈的概念大家应该都有所了解,它是一种后进先出的数据结构。
当前 Activity 启动另一个 Activity 时,该新 Activity 会被推送到堆栈顶部,成为焦点所在。 前一个Activity 仍保留在堆栈中,但是处于停止状态。Activity 停止时,系统会保持其用户界面的当前状态。 用户按“返回”按钮时,当前 Activity 会从堆栈顶部弹出(Activity 被销毁),而前一个 Activity 恢复执行(恢复其 UI 的前一状态)。 堆栈中的Activity 永远不会重新排列,仅推入和弹出堆栈:由当前 Activity 启动时推入堆栈;用户使用“返回”按钮退出时弹出堆栈。因此,返回栈以“后进先出”对象结构运行。
来看下官方给的示例图:
在实际项目中我们需要根据不同的需求为每个活动指定合适的启动模式。启动模式有4中:standard、singleTop、singleTask和singleInstance。
那么该如何定义活动的启动模式呢?
1. 使用清单文件
在清单文件中声明 Activity 时可以使用 元素的 launchMode 属性指定 Activity 应该如何与任务关联。
launchMode 属性指定有关应如何将 Activity 启动到任务中的指令。launchMode 可选值就是上面列出的四种启动模式。
2. 使用 Intent 标志
启动 Activity 时,您可以通过在传递给 startActivity() 的 Intent 中加入相应的标志,修改 Activity 与其任务的默认关联方式。可用于修改默认行为的标志包括:
FLAG_ACTIVITY_NEW_TASK:与 "singleTask"launchMode 值相同的行为
FLAG_ACTIVITY_SINGLE_TOP:与 "singleTop"launchMode 值相同的行为
FLAG_ACTIVITY_CLEAR_TOP:如果正在启动的 Activity 已在当前任务中运行,则会销毁当前任务顶部的所有Activity,并通过 onNewIntent() 将此 Intent 传递给 Activity已恢复的实例(现在位于顶部),而不是启动该 Activity 的新实例。 产生这种行为的 launchMode 属性没有值。
FLAG_ACTIVITY_CLEAR_TOP 通常与 FLAG_ACTIVITY_NEW_TASK
结合使用。一起使用时,通过这些标志,可以找到其他任务中的现有 Activity,并将其放入可从中响应 Intent 的位置。
注:某些适用于清单文件的启动模式不可用作 Intent 标志,同样,某些可用作 Intent 标志的启动模式无法在清单文件中定义。
standard启动模式
standard是活动的默认启动模式,如果不显示指定启动模式,那么所有的活动都会自动使用这种启动模式。系统在启动 Activity 的任务中创建 Activity 的新实例并向其传送 Intent。Activity 可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例。
看个示例:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate: QStandardActivity" + this.toString());
setContentView(R.layout.activity_qstandard);
standard_Button = findViewById(R.id.standard_button);
standard_Button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(QStandardActivity.this, QStandardActivity.class);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
});
}
布局很简单就是一个按钮,我们onCreate方法中打印当前活动的实例来验证。
启动后,观察log输出:
singleTop启动模式
我们也可以称之为栈顶复用模式。如果当前任务的顶部已存在 Activity 的一个实例,则系统会通过调用该实例的 onNewIntent() 方法向其传送 Intent,而不是创建 Activity 的新实例。Activity 可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例。
注意:是当前任务的顶部已存在 Activity 的一个实例。也就是说当且仅当启动的 Activity 和上一个 Activity 一致的时候才会通过调用 onNewIntent() 方法重用 Activity
如果没有位于栈顶,那么仍然会创建新的实例,并添加到任务栈。
修改QStandardActivity启动模式为singleTop
运行程序后会发现不论我们点击多少次都不会有新的log信息出现。前面也说了QStandardActivity不在栈顶的时候,还是会创建新的实例的。
新建个活动QTestActivity(QStandardActivity重命名为QLaunchModeActivity).代码和QLaunchModeActivity一样。
在QLaunchModeActivity中启动QTestActivity,当QTestActivity启动后在点击按钮启动QLaunchModeActivity。
QLaunchModeActivity:
QTestActivity:
运行:
logcat输出:
点击按钮:
logcat输出:
在此点击按钮启动QLaunchModeActivity:
生命周期的变化:
singleTask启动模式
该模式是对SingleTop的进一步加强,系统创建新任务并实例化位于新任务底部的Activity。但是,如果该 Activity 的一个实例已存在于一个单独的任务中,则系统会通过调用现有实例的 onNewIntent() 方法向其传送 Intent,而不是创建新实例。一次只能存在 Activity 的一个实例。在这个过程中,会将 该Activity之上的所有活动全部出栈。
注意:尽管 Activity 在新任务中启动,但是用户按“返回”按钮仍会返回到前一个 Activity
同理修改启动模式:
还是的重复贴图了:
singleInstance启动模式
与 “singleTask” 相同,只是系统不会将任何其他 Activity 启动到包含实例的任务中。该 Activity 始终是其任务唯一仅有的成员。
在新栈中创建该 Activity 的实例,且应用共享该栈中的该 Activity 实例。该模式的 Activity 实例如果已经存在于某个栈中,任何应用再激活该 Activity 时都会重用该栈中的实例。
启动模式设置为 singleInstance的Activity 启动新的 Activity,新的 Activity 不会在当前的 Task 里面,而是会回到上一个 Task 里面。
适用场景
singleTop: 适合接收通知启动的内容显示页面。
singleTask: 适合作为程序入口点。
singleInstance: 适合需要与程序分离开的页面。
大家都可能遇到过这样的场景,我们打开一个程序后,然后去浏览其他的程序,再次返回该程序的时候会发现又回到了起始页面。
这是因为如果用户长时间离开任务,则系统会清除所有 Activity 的任务,根 Activity 除外。当用户再次返回到任务时,仅恢复根Activity。系统这样做的原因是,经过很长一段时间后,用户可能已经放弃之前执行的操作,返回到任务是要开始执行新的操作。
我们可以通过下列几个 Activity 属性修改此行为:
alwaysRetainTaskState
如果在任务的根 Activity 中将此属性设置为 “true”,则不会发生上面所述的默认行为。即使在很长一段时间后,任务仍将所有 Activity 保留在其堆栈中
clearTaskOnLaunch
如果在任务的根 Activity 中将此属性设置为 “true”,则每当用户离开任务然后返回时,系统都会将堆栈清除到只剩下根 Activity。 换而言之,它与 alwaysRetainTaskState 正好相反。 即使只离开任务片刻时间,用户也始终会返回到任务的初始状态。
finishOnTaskLaunch
此属性类似于 clearTaskOnLaunch,但它对单个 Activity 起作用,而非整个任务。 此外,它还有可能会导致任何 Activity 停止,包括根 Activity。 设置为 “true” 时,Activity 仍是任务的一部分,但是仅限于当前会话。如果用户离开然后返回任务,则任务将不复存在。