App中Activity归属哪个Task的逻辑 一

张开发
2026/4/23 15:36:15 15 分钟阅读

分享文章

App中Activity归属哪个Task的逻辑 一
首先得了解下Activity的启动模式Activity的启动模式在 Android 中,Activity 的启动模式(Launch Mode)决定了 Activity 实例的创建机制以及它们在任务栈(Task)中的存放方式。五大启动模式通常可以通过AndroidManifest.xml中的android:launchMode属性来设置,常见的启动模式有以下 4 种(在 Android 12 中又新增了第 5 种):standard(标准模式/默认模式)这是 Android 默认的启动模式。每次启动 Activity,系统都会创建一个新的实例,并将它压入启动它的那个任务栈中。不管这个实例是否已经存在于栈中,系统绝不吝啬,照建不误。生命周期:onCreate-onStart-onResume适用场景:绝大多数普通的展示类页面。例如:邮件列表点击进入一封邮件的详情页,再点一封又进一个详情页,这两个详情页是两个独立的实例。singleTop(栈顶复用模式)如果要启动的 Activity已经位于当前任务栈的栈顶,系统就不会创建新的实例,而是直接复用这个栈顶实例,并回调它的onNewIntent()方法。如果该 Activity 存在于栈中但不在栈顶,系统依然会创建一个全新的实例。生命周期:复用时调用onPause-onNewIntent-onResume。适用场景:为了避免由于用户连续点击或频繁刷新而导致在栈顶创建多个相同的页面。例如:新闻App的“推送通知点击处理页面”、搜索框弹出的“搜索结果页面”。singleTask(栈内复用模式)这是一种具有“清理”功能的模式。系统会在指定的任务栈(由taskAffinity决定)中寻找该 Activity 的实例。如果找不到:创建新实例并入栈。如果找到了:系统会把位于该 Activity 上方的所有其他 Activity 统统出栈(销毁),让这个 Activity 重新回到栈顶,并回调它的onNewIntent()方法。生命周期:复用时调用onNewIntent-onRestart-onStart-onResume。适用场景:App 的主界面(MainActivity)。不管用户在 App 里进入了多深的子页面,只要触发了“返回首页”的操作,主页上方所有的页面都会被销毁,主页干干净净地回到用户面前。singleInstance(全局单例模式)最霸道、最极端的启动模式。系统会为该 Activity单独创建一个全新的任务栈,并且这个任务栈里有且只能有它这一个实例。无论哪个应用再来启动它,都会复用这个全局唯一的实例,并回调onNewIntent()。适用场景:需要与整个系统完全独立、全局共享的页面。例如:来电接听界面、闹钟响铃界面。singleInstancePerTask(Android 12 新增)结合了singleTask和singleInstance的部分特性。它允许该 Activity 成为某个任务栈的根节点(类似singleTask创建新栈),但允许在系统的不同任务栈中各存在一个该 Activity 的实例。适用场景:分屏模式或多窗口模式下,同一应用在不同屏幕区域打开多个独立的工作流实例影响启动模式的其他因素除了在AndroidManifest.xml中静态配置的android:launchMode之外,Android 系统在决定一个 Activity 如何启动、放入哪个任务栈(Task)时,还会受到很多其他因素的影响。这些因素往往具有更高的优先级,甚至可以完全覆盖静态配置。主要可以总结为以下五个维度:Intent Flags (动态标志位)通过Intent.addFlags()或setFlags()设置,它的优先级高于 Manifest 中的静态配置。FLAG_ACTIVITY_NEW_TASK:行为类似于singleTask的前半部分。它会寻找与新 Activity 匹配的任务栈(根据taskAffinity),如果找到就把目标栈带到前台;如果找不到就创建一个新栈。注意:单纯使用此 Flag 不会有singleTask的“清理上方 Activity”的效果,通常需要配合 **FLAG_ACTIVITY_CLEAR_TOP**使用。FLAG_ACTIVITY_CLEAR_TOP:如果目标 Activity 已经在栈中,系统会销毁它上方的所有 Activity,让它回到栈顶。如果结合standard模式,目标 Activity 本身也会被销毁并重新创建;如果结合singleTop或FLAG_ACTIVITY_SINGLE_TOP,则会复用该实例并调用onNewIntent。FLAG_ACTIVITY_SINGLE_TOP:完全等同于在 Manifest 中配置singleTop。FLAG_ACTIVITY_CLEAR_TASK:极其暴力的 Flag。必须和FLAG_ACTIVITY_NEW_TASK一起使用。它会在启动 Activity 之前,清空目标任务栈中的所有Activity,使得这个新启动的 Activity 成为该栈的唯一成员(新的根节点)。taskAffinity (任务栈亲和性)taskAffinity决定了 Activity “倾向于”属于哪个任务栈。默认情况:同一个 App 的所有 Activity 默认具有相同的taskAffinity(也就是 App 的包名),因此它们默认都在同一个栈里。taskAffinity本身不改变启动模式,但它是singleTask、singleInstance以及FLAG_ACTIVITY_NEW_TASK生效的重要基石。举例:如果给一个standard模式的 Activity 设置了不同的taskAffinity,直接启动它并不会创建新栈。但如果在 Intent 中加上了FLAG_ACTIVITY_NEW_TASK,系统就会根据它特殊的taskAffinity为它创建一个全新的任务栈。Manifest 中的 Task 控制属性这些属性配置在activity标签中,主要用于控制任务栈的清理和重排行为:allowTaskReparenting:如果设为true,当某个 Activity 最初被“借用”到别的任务栈里运行,但之后它所属亲和性(taskAffinity)对应的任务栈被带到前台时,这个 Activity 会从当前栈“迁回”到自己的亲和性栈。例子:App 有两个任务栈亲和性:com.example.main(主流程)和com.example.work(工作流)。WorkEntryActivity的taskAffinity=com.example.work,并开启allowTaskReparenting=true。用户在主流程里(com.example.main任务栈)点按钮进入WorkEntryActivity,它会先被启动在主栈里。之后用户通过桌面/最近任务把“工作流任务栈(com.example.work)”切到前台时,系统会把这个WorkEntryActivity从主栈挪到工作流栈里,让它回到“该待的地方”。clearTaskOnLaunch(只能设置在栈底的根 Activity):如果设为true,只要用户离开这个任务栈(比如回到桌面),下次再返回该栈时,系统会清除除了根 Activity 之外的所有 Activity。这保证了用户每次进入该任务栈,看到的都是初始状态。alwaysRetainTaskState(也作用于根 Activity):默认情况下,如果一个任务栈在后台停留时间过长(通常是 30 分钟),系统会清理掉除了根 Activity 以外的其他页面。如果将此属性设为true,系统会尽全力保留栈内的所有 Activity 状态,无论放了多久。启动源的上下文 (Caller Context)是谁在发起启动,也会影响启动模式:Activity Context:默认行为,将新页面压入当前 Activity 所在的栈。非 Activity Context(如 Service、Application、BroadcastReceiver):因为这些组件本身没有任务栈,系统无法将新 Activity 压入“当前栈”。系统底层会强制为 Intent 添加FLAG_ACTIVITY_NEW_TASK,从而创建一个新栈(或寻找亲和性栈)来容纳这个 Activity。5. 多窗口与多显示器属性 (Windowing Display)在 Android 7.0 引入多窗口,以及后续引入多屏幕支持后,窗口状态也会影响启动结果:ActivityOptions.setLaunchDisplayId():指定 Activity 在哪个物理屏幕(如外接显示器、折叠屏的外屏)上启动,这可能会导致系统为其分配或建立独立于主屏幕的任务栈。FLAG_ACTIVITY_LAUNCH_ADJACENT:此标志仅用于分屏多窗口模式。如果可能,新创建的 Activity 将显示在启动它的 Activity 旁边。此标志只能与 {@link #FLAG_ACTIVITY_NEW_TASK} 结合使用。此外,如果希望创建现有 Activity 的新实例,则必须设置 {@link #FLAG_ACTIVITY_MULTIPLE_TASK}。源码分析一切的一切,要从ActivityStarter::startActivityInner开始分析,在该方法中:// frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.javaintstartActivityInner(finalActivityRecord r,ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession,IVoiceInteractor voiceInteractor,intstartFlags,ActivityOptions options,Task inTask,TaskFragment inTaskFragment,BalVerdict balVerdict,NeededUriGrants intentGrants,intrealCallingUid){// 将传入的参数保存到 ActivityStarter 的成员变量中,方便内部方法调用。setInitialStat

更多文章