从四个视角理解Activity的启动模式

    科技2025-08-22  18

    本文主要从Android操作系统、程序员视角、架构师视角、用户视角四个方面来更好地理解Activity的启动模式。

    1、Android软件体系架构

    1.1 Android的软件体系结构

    静态视角:

    1.2 Tasks

    Activity代码属于Application,但是Task属于Android操作系统。Task是跨应用的,属于整个Android操作系统。

    1.3 怎样查看Tasks

    注意点击menu按钮显示的不是多个Activity,而是整个系统的Tasks。

    也可以使用adb查看当前Android系统启动了哪些Tasks,以及每个Task保护哪些Actvity:

    adb shell dumpsys activity activities | sed -En -e ‘/Stack #/p’ -e ‘/Running activities/,/Run #0/p’

    2、Activity的启动方式

    2.1 Task的启动方式(恢复)

    恢复和新建对应的Activity生命周期时不一样的。

    2.2 Task启动方式(新建)

    1、Scheme 规则:schema完整路径:schema://host:port/path/query authority 即 host:port query 即 queryParameter 举例:xl://goods:8888/goodsDetail?goodsId=10011002 schema=xl host=goods port=8888 (必须是int型哦) authority=goods:8888 path=/goodsDetail query=goodsId=10011002

    2、第三方应用start

    2.3 Laucher启动

    Laucher启动又分两种情况: 1、Task不存在 2、Task已经存在

    3、Activity & Fragment的生命周期

    3.1 Activity和Fragment

    Fragment是Android3.0后引入的一个新的API,他出现的初衷是为了适应大屏幕的平板电脑, 当然现在他仍然是平板APP UI设计的宠儿,而且我们普通手机开发也会加入这个Fragment, 我们可以把他看成一个小型的Activity,又称Activity片段!

    3.2 Activity的生命周期

    3.3 相邻状态之间的区别

    Activity启动的一个原则是尽快让用户看到新的页面。 1.onCreate和onStart之间有什么区别? (1)可见与不可见的区别。前者不可见,后者可见。 (2)执行次数的区别。onCreate方法只在Activity创建时执行一次,而onStart方法在Activity的切换以及按Home键返回桌面再切回应用的过程中被多次调用。因此Bundle数据的恢复在onStart中进行比onCreate中执行更合适。 (3)onCreate能做的事onStart其实都能做,但是onstart能做的事onCreate却未必适合做。如前文所说的,setContentView和资源初始化在两者都能做,然而想动画的初始化在onStart中做比较好。 2.onStart方法和onResume方法有什么区别? (1)是否在前台。onStart方法中Activity可见但不在前台,不可交互,而在onResume中在前台,可交互。 (2)职责不同,onStart方法中主要还是进行初始化工作,而onResume方法,根据官方的建议,可以做开启动画和独占设备的操作。 3.onPause方法和onStop方法有什么区别? (1)是否可见。onPause时Activity可见,onStop时Activity不可见,但Activity对象还在内存中。 (2)在系统内存不足的时候可能不会执行onStop方法,因此程序状态的保存、独占设备和动画的关闭、以及一些数据的保存最好在onPause中进行,但要注意不能太耗时。 (3)新Activity并不是百分百启动成功的,启动新Activity的过程中可能crash掉,这时旧Activity的onStop方法是不会调用的。

    Android开发文档: Because onPause() is the first of the three(onPause(),onStop(),onDestroy()), once the activity is created, onPause() is the last method that’s guaranteed to be called before the process can be killed—if the system must recover memory in an emergency, then onStop() and onDestroy() might not be called. Therefore, you should use onPause() to write crucial persistent data (such as user edits) to storage. However, you should be selective about what information must be retained during onPause(), because any blocking procedures in this method block the transition to the next activity and slow the user experience.

    Note: Because onSaveInstanceState() is not guaranteed to be called, you should use it only to record the transient state of the activity (the state of the UI)—you should never use it to store persistent data. Instead, you should use onPause() to store persistent data (such as data that should be saved to a database) when the user leaves the activity. 关键的持久数据应该在onPause()方法中保存,因为onPause()方法是进程被杀死之前保证会执行的最后一个方法。当然保存的数据必须慎重,因为如果方法阻塞,会影响系统响应时间,影响用户体验。 4.onStop方法和onDestroy方法有什么区别? onStop阶段Activity还没有被销毁,对象还在内存中,此时可以通过切换Activity再次回到该Activity,而onDestroy阶段Acivity被销毁

    3.4 onNewIntent的生命周期

    1、只对singleTop,singleTask,singleInstance有效,因为standard每次都是新建,所以不存在onNewIntent; 2、只对startActivity有效,对于从Navigation切换回来的恢复无效,即从Navigation切换回来时不会调用onNewIntent; 3. 当调用到onNewIntent(intent)的时候,需要在onNewIntent(intent) 中使用setIntent(intent)赋值给Activity的Intent,否则,后续的getIntent()都是得到老的Intent。

    4、Activity的4种启动模式

    standard (ActivityInfo.LAUNCH_MULTIPLE)singleTop (ActivityInfo.LAUNCH_SINGLE_TOP)singleTask (ActivityInfo.LAUNCH_SINGLE_TASK)singleInstance (ActivityInfo.LAUNCH_SINGLE_INSTANCE)

    4.1 standard模式(默认启动模式)

    系统在启动 Activity 的任务中创建 Activity 的新实例并向其传送 Intent。Activity 可以多次实例化,不管这个实例是否已经存在,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例。这种模式的 Activity 被创建时它的 onCreate、onStart 都会被调用。这是一种典型的多实例实现,一个任务栈中可以有多个实例,每个实例也可以属于不同的任务栈。在这种模式下,谁启动了这个 Activity,那么这个 Activity 就运行在启动它的那个 Activity 所在的栈中。

    a、当从非Activity的context启动activity时,需要带NEW_TASK的flag; b、当启动一个带有affinity的activity(与系统默认的不一致)并且带有NEW_TASK标记,如果这个带有affinity的activity已经有实例存在该task,则不会重新创建; c、如果从应用内启动的standard activity的Affinity就是App默认的Affinity(app的包名),则会每次新建一个实例; d、当启动 Activity 的 intent 包含 FLAG_ACTIVITY_NEW_TASK标记时affinity发挥作用。 因此启动带有不同的affinity的standard activity(或者singleTop activity)时并不会把新activity放入一个新的task栈,必须同时加上NEW_TASK标记才放入一个新的task栈;启动带有不同的affinity的singleTask activity(或者singleInstance activity)会把新activity放入一个新的task栈。

    4.2 singleTop模式(栈顶复用模式)

    一个singleTop Activity 的实例可以无限多,唯一的区别是如果在栈顶已经有一个相同类型的Activity实例,Intent不会再创建一个Activity,而是通过onNewIntent()将Intent发送到栈顶的Activity对象。

    如果以singleTop模式启动的activity的一个实例已经存在于任务桟中,但是不在桟顶,那么它的行为和standard模式相同,也会创建多个实例。

    singleTop模式的Activity的启动流程图:

    4.3 singleTask模式(栈内复用模式)

    这是一种单实例模式,在这种模式下,只要 Activity 在一个栈中存在,那么多次启动此 Activity 都不会重新创建实例,和 singleTop一样,系统也会回调其 onNewIntent。当一个具有 singleTask 模式的Activity请求启动后,比如 Activity A,系统首先会寻找是否存在 A 想要的任务栈,如果不存在,就重新创建一个任务栈,然后创建 A 的实例后把 A 放到栈中。如果存在 A 所需的任务栈,这时要看 A 是否在栈中有实例存在,如果有实例存在,那么系统会销毁该Activity上的所有Activity,最终让该Activity实例处于栈顶,并调用它的 onNewIntent 方法,如果实例不存在,就创建 A 的实例并把 A 压入栈中 。

    singleTask模式的Activity的启动流程图:

    4.4 singleInstance模式(全系统单例模式)

    与 “singleTask” 相似,唯一不同的是系统不会将任何其他 Activity 启动到包含该实例的任务中。该 Activity 始终是其任务唯一的成员;由该 Activity 启动的任何 Activity 都会在其他的任务中打开。 也就是此种模式的 Activity 只能单独地位于一个任务栈中。

    启动singleInstance模式的Activity总是在新的任务中开启,并且这个新的任务中有且只有这一个实例,也就是说被该实例启动的其他Activity会自动运行于另一个任务中。当再次启动该Activity的实例时,会重用已存在的任务和实例,并且会调用这个实例的onNewIntent()方法,将Intent实例传递到该Activity实例中。

    与singleTask模式的区别: singleTask模式的Activity是可以有多个实例的,只要这些Activity在不同的任务栈中即可,例如,应用A启动了一个启动模式为 singleTask 的ActivityA,应用B又通过Intent 想要启动一个ActivityA,此时由于应用A和应用B都有自己的任务栈,因此,在这两个任务栈中分别有一个ActivityA实例。而singleInstance 能保证Activity在系统中只有一个实例,不管多少应用要启动该Activity,这个Activity有且只有一个。

    4.5 Intent Activity Flag(Intent.FLAG_ACTIVITY_XXXXX)

    <activity> 清单元素中的属性(包括taskAffinity、launchMode、allowTaskReparenting、clearTaskOnLaunch等等)以及您传递给 startActivity() 的 intent 中的标记(包括FLAG_ACTIVITY_NEW_TASK、FLAG_ACTIVITY_CLEAR_TOP、FLAG_ACTIVITY_SINGLE_TOP等等)结合使用。

    可以对Intent.FLAG_ACTIVITY_XXXXX这些标记进行分类: NEW_TASK只对standard和singleTop有效。 MULTIPLE_TASK和NEW_DOCUMENT只对standard有效。

    4.6 亲和性(taskAffinity)

    “亲和性”表示 Activity 倾向于属于哪个任务。默认情况下,同一应用中的所有 Activity 彼此具有亲和性。因此,在默认情况下,同一应用中的所有 Activity 都倾向于位于同一任务。不过,您可以修改 Activity 的默认亲和性。在不同应用中定义的 Activity 可以具有相同的亲和性,或者在同一应用中定义的 Activity 也可以被指定不同的任务亲和性。

    您可以使用 <activity> 元素的 taskAffinity 属性修改任何给定 Activity 的亲和性。

    taskAffinity 属性采用字符串值,该值必须不同于 <manifest> 元素中声明的默认软件包名称,因为系统使用该名称来标识应用的默认任务亲和性。

    亲和性可在两种情况下发挥作用:

    启动 Activity 的 intent 包含 FLAG_ACTIVITY_NEW_TASK 标记时。 默认情况下,新 Activity 会启动到调用 startActivity() 的 Activity 的任务中。它会被推送到调用方 Activity 所在的返回堆栈中。但是,如果传递给 startActivity() 的 intent 包含 FLAG_ACTIVITY_NEW_TASK 标记,则系统会寻找其他任务来容纳新 Activity。通常会是一个新任务,但也可能不是。如果已存在与新 Activity 具有相同亲和性的现有任务,则会将 Activity 启动到该任务中。如果不存在,则会启动一个新任务。 如果此标记导致 Activity 启动一个新任务,而用户按下主屏幕按钮离开该任务,则必须为用户提供某种方式来返回到该任务。有些实体(例如通知管理器)总是在外部任务中启动 Activity,而不在它们自己的任务中启动,因此它们总是将 FLAG_ACTIVITY_NEW_TASK 添加到传递给 startActivity() 的 intent 中。如果您的 Activity 可由外部实体调用,而该实体可能使用此标记,请注意用户可以通过一种独立的方式返回到所启动的任务,例如使用启动器图标(任务的根 Activity 具有一个 CATEGORY_LAUNCHER intent 过滤器;请参阅下面的启动任务部分)。当 Activity 的 allowTaskReparenting 属性设为 “true” 时。 在这种情况下,一旦和 Activity 有亲和性的任务进入前台运行,Activity 就可从其启动的任务转移到该任务。 举例来说,假设一款旅行应用中定义了一个报告特定城市天气状况的 Activity。该 Activity 与同一应用中的其他 Activity 具有相同的亲和性(默认应用亲和性),并通过此属性支持重新归属。当您的某个 Activity 启动该天气预报 Activity 时,该天气预报 Activity 最初会和您的 Activity 同属于一个任务。不过,当旅行应用的任务进入前台运行时,该天气预报 Activity 就会被重新分配给该任务并显示在其中。

    4.7 总结

    FLAG_ACTIVITY_NEW_TASK只是标记可以在新的task中启动Activity,但是否真的会启动一个新的task则根据具体情况而定,如果taskAffinity指定的task已经存在,则不会新启动一个task。

    taskAffinity“亲和性”表示 Activity 倾向于属于哪个task。

    对于singleInstance,不管是否指定新的taskAffinity,如果不存在该Activity的实例,则都会创建一个新的task,然后在该task中创建该Activity的实例。

    xml 中无法指定 FLAG_ACTIVITY_CLEAR_TOP, 而 flag 中无法指定 singleInstance

    对于"singleTask"模式,Tasks and Back Stack 中说:

    The system creates a new task and instantiates the activity at the root of the new task…

    这是不严谨的. 因为同一个应用中如果不指定 ‘taskAffinity’ 的话, 那么 ‘singleTask’ 会在默认的 task (即 MAIN/LAUNCHER 所在的 task) 中启动。

    official doc 中还说:

    Since activities with “singleTask” or “singleInstance” launch modes can only be at the root of a task…

    显然, 这也是错误的. 当你从 MAIN/LAUNCHER 的 activity 中启动一个没指定任何 affinity 的 ‘singleTask’ activity 时, 这个新启动的 activity 会在当前 task 中被创建(见上描述), 并且是在 MAIN/LAUNCHER 的上面. 因此, ‘singleTask’ 的 activity 未必一定是 the root activity of a task.

    standard 和 singleTop 是一类. 它们都可以被创建多个 instance. 这两者的区别是: standard 无论何时接到 Intent, 都会在调用者的 task 中 push 一个新的 activity 放在尾部。 singleTop 当调用者当前 task 的顶部是 Intent 要启动的 activity 时, 则当前顶部的 activity 会调用 onNewIntent 并且不会启动新的 activity. 否则, 也会在调用者的 task 中 push 一个新的 activity 放在尾部。 singleTask 和 singleInstance 是一类. singleTask的activity在整个app中只有一个instance,singleInstance的activity在系统中只能存在一个instance. 这两者的区别是: singleTask 根据 affinity 决定在哪个 task 中启动. 如果存在该 affinity 所指定的 task, 则在该 task 中启动, 否则会新建一个该 affinity 的 task, 并在其中启动activity. singleInstance 被启动的 activity 只能单独存在于一个独立的 task 中(即task 中只有一个activity). 如果从该 task 再次启动其它 activity, 则新启动的 activity 会被放到其它 task 中. 其它规则同 singleTask。

    5、启动模式应用场景

    5.1 启动模式的应用场景

    5.2 课程总结

    参考: https://developer.android.google.cn/guide/components/activities/tasks-and-back-stack

    Android中Activity四种启动模式和taskAffinity属性详解

    https://github.com/ushuaia/TaskDemo https://github.com/gnorsilva/Activities-LaunchMode-demo

    Processed: 0.023, SQL: 8