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 是否相同会直接影响到后台是否在同一张任务卡片里。

    参考: