Android 的四种启动模式,分别是 standard、singleTop、singleTask 和 singleInstance。四种启动模式都跟栈有关。

这里简单概况自己觉得比较容易一下子看懂的说法。

当没有在 AndroidManifest.xml 里为 <activity> 配置 android:launchmode 时,所有的 Activity 默认都是以 standard 模式启动的。

预先提醒好,Task 和 栈 是两个概念,最简单粗暴的说:

1. 栈是你看不见摸不着的,多个栈可以存在于同一个 Task 里;

2. 另一个 Task 启动后,你能在最近任务里看到它。

结尾有一个太长不看(虽然并不长)的总结。

standard - 默认模式

简单的说,就是你每次启动这个 Activity 时,无论情况如何,都会创建一个新的实例。比方说你打开了 MainActivity,这个 Activity 里有一个按钮 StartButton,点击后会完整启动 MainActivity。

此时的栈点击前:

  • MainActivity // 原先的
  • ...

点击后:

  • MainActivity // 新的
  • MainActivity // 原先的
  • ...

即使栈里原本就有 MainActivity,此时还是会创建一个新的 MainActivity 实例,并且按顺序调用 onCreate()、onStart()、onResume(),与新的完全一样。

有关 taskAffinity,留到后面讲。

singleTop - 栈顶复用

跟 standard 不同,在上面的程序中,如果设置了 singleTop,并且当前 Activity 处于栈顶,在点击 StartButton 后,并不会重新创建实例,而是保留原本的实例(但会调用 onNewIntent())

也就是这个 Activity 在前台运行时,如果你还想启动这同一个 Activity,则并不会跟 standard 模式一样再创建实例,即并不会调用 onCreate()、onStart()

此时的栈点击前:

  • MainActivity // 原先的
  • ...

点击后:

  • MainActivity // 原先的 &“新”的
  • ...

但是此时会调用 MainActivity的 onNewIntent() 方法。

对了,如果前面前提不成立,当前 Activity 不在栈顶呢?那就跟 standard 模式一样重新创建实例,比如:

此时的栈点击前:

  • OtherActivity
  • MainActivity // 原先的
  • ...

点击后:

  • MainActivity // 新的
  • OtherActivity
  • MainActivity // 原先的
  • ...

有关 taskAffinity,留到后面讲。

singleTask - 栈内复用

其实看到名字可以以此类推了,singleTask 模式下,只要栈里有 MainActivity,就会直接将 MainActivity 上方的所有 Activity 全部出栈,让 MainActivity 回到栈顶。

“这个栈里只能容得下一个👴!”

此时的栈点击前:

  • AnotherActivity
  • OtherActivity
  • MainActivity // 原先的
  • ...

点击后:

  • MainActivity // 原先的 &“新”的
  • ...

MainActivity 上方的两个 Activity 都被出栈了。

这一部分需要讨论 taskAffinity(终于)

taskAffinity 也是 <activity> 的一个属性,即 android:taskAffinity。未设置的情况下,其默认值为包名。

是的,如果设置了与包名不一样的 taskAffinity,其对应的 Activity 的 Task 就发生了改变。下面假定 Activity 启动顺序为:

  1. 未设置 taskAffinity 的 OtherActivity(默认 standard 模式)
  2. 设置了 taskAffinity 的 MainActivity(指定 singleTask 模式)
  3. 未设置 taskAffinity 的 AnotherActivity(默认 standard 模式)

所以:

  1. 在启动了 OtherActivity 后启动 MainActivity,由于 MainActivity 的启动模式是 singleTask,此时 Android 会查找其 taskAffinity。在这里会发现它的 taskAffinity 并非与包名相同,因此会创建一个新的 Task,并将 MainActivity 放入其中(此时 MainActivity 与 OtherActivity 处于不同的 Task 中);
  2. 然后,启动 AnotherActivity。因为 AnotherActivity 使用的是 standard 模式,Android 无需去寻找其 taskAffinity 的值,只要跟着之前的 Activity 就行,因此此时的 AnotherActivity 会进入 MainActivity 所在的栈。

之前:

  • OtherActivity
  • ...

之后:

旧栈

  • OtherActivity
  • ...

新栈

  • AnotherActivity
  • MainActivity
  • ...

这里可以看到,设置了 taskAffinity 的 MainActivity 启动后,由于切换到了新栈,启动 AnotherActivity 的话,它不会回到旧栈,而是在新栈中成为栈顶。

singleInstance - 全局唯一

跟 singleTask 差不多,但是使用此启动模式创建的 Task,整个 Task 里只有它自己一个。直接上例子可能会好一点。

我们预先设定 OtherActivity 使用 standard 模式,MainActivity 使用 singleInstance 模式。

当两者 taskAffinity 相同时

此时会创建新的 Task 且独一无二,点击启动 MainActivity 前:

  • OtherActivity
  • ...

点击后:

原来的 Task

  • OtherActivity
  • ...

新的 Task

  • MainActivity
  • ...

但是这里注意,如果我在 MainActivity 里再想启动 AnotherActivity 时:

原来的 Task

  • AnotherActivity
  • OtherActivity
  • ...

新的 Task

  • MainActivity
  • ...

是的,新的 Activity 并不会进入到新的 Task 里, 所以说 singleInstance 创建的 Task 是独一无二的。

当两者 taskAffinity 不同时

表现上与 taskAffinity 相同时一致,但是可以在最近任务里看到。

总结

简单来说:

standard

你想创建新实例就创建吧,随你,叠千层饼都行。

singleTop

如果你想启动你面前(栈顶)的 Activity 的话,除了会调用它的 onNewIntent() 外没什么变化。除此之外,跟 standard 一样。

singleTask

taskAffinity 相同的时候(不设置的时候也相同),如果之前开过这个 Activity(还在栈里),就直接把它拽到最前面,其他的统统废掉(栈中在它上方的全部出栈);

taskAffinity 不相同的时候,启动这种 Activity 后,它会脱离于原来的 Task,新开一个,而且新顾客也会跟着它到新的地方去(后续启动的 Activity 会跟随着进入到新的 Task)。而且你能在最近任务里看到它有别于原来的 Activity(后台里任务卡片大于一张)。

singleInstance

taskAffinity 相同的时候(不设置的时候也相同),启动这种 Activity 后,它会脱离于原来的 Task,新开一个,但是新顾客不会跟它走,新顾客还是会回到老的地方去(后续启动的 Activity 会在原来的 Task 中启动);

taskAffinity 不相同的时候,启动这种 Activity 后,它跟 taskAffinity 相同的时候一样,唯一不同的是你能在最近任务里看到它有别于原来的 Activity(后台里任务卡片大于一张)。

Tips

  • taskAffinity 是否相同会直接影响到后台是否在同一张任务卡片里。

参考: