Task
Activity 启动模式
- standard(标准模式):这是默认的启动模式。在这种模式下,每次启动一个 Activity,都会创建一个新的实例。例如,假设应用中有一个 Activity A,当从一个地方多次启动 Activity A 时,会创建多个 Activity A 的实例并添加到任务栈中。就像在一个购物应用中,商品详情页面可能是一个 Activity,每次用户从不同的商品列表进入商品详情页面时,都会创建一个新的商品详情 Activity 实例。
- singleTop(栈顶复用模式):如果要启动的 Activity 已经位于任务栈的栈顶,那么就不会重新创建这个 Activity 的实例,而是会调用这个栈顶 Activity 的 onNewIntent () 方法。例如,在一个浏览器应用中,浏览器的主页面 Activity 如果采用 singleTop 模式,当用户在浏览器主页面通过搜索框再次打开这个主页面(可能是因为搜索结果的跳转),就不会重新创建主页面 Activity,而是会复用栈顶的这个 Activity 实例,并且通过 onNewIntent () 方法传递新的 Intent,这样可以更新页面内容等操作。
- singleTask(栈内复用模式):当启动一个 Activity 时,如果任务栈中已经存在这个 Activity 的实例,那么会将这个 Activity 之上的其他 Activity 全部清除,然后复用这个已存在的 Activity 实例。例如,在一个应用中有登录 Activity 和主界面 Activity,登录 Activity 采用 singleTask 模式。当用户从主界面退出登录后,再次登录时,登录 Activity 不会重新创建,而是会复用之前的登录 Activity 实例,并且清除它上面的其他 Activity(如主界面 Activity),这样可以保证登录流程的独立性和数据的一致性。
- singleInstance(单实例模式):这种模式下,会为这个 Activity 创建一个单独的任务栈。这个 Activity 在整个系统中只有一个实例。例如,在一个应用中调用系统的拨号 Activity(假设这个拨号 Activity 是通过自定义的方式设置为 singleInstance 模式),这个拨号 Activity 会在自己单独的任务栈中,当其他应用也需要调用拨号功能时,会复用这个已经存在的拨号 Activity 实例,而不是重新创建。这样可以保证拨号功能的独立性,并且方便不同应用之间共享这个 Activity。
Task
Task属于操作系统,Task是activity的堆栈
在 Android 中,Task(任务) 是一个核心概念,用于管理用户交互的一系列相关组件(主要是 Activity),它本质上是一个Activity 堆栈(Stack),遵循 “后进先出(LIFO)” 原则。尽管 Task 是 Android 系统(而非底层操作系统内核)管理的组件,但它深刻影响着应用的用户体验和组件生命周期。
Task 的核心本质
- Activity 堆栈Task 以堆栈形式组织 Activity:
- 当用户启动一个新 Activity 时,它会被压入当前 Task 的栈顶(成为用户可见的前台 Activity)。
- 当用户按返回键时,栈顶 Activity 出栈,前一个 Activity 恢复为前台。
- 当栈中所有 Activity 出栈后,Task 为空,系统会销毁该 Task 以释放资源。
- 例:打开「微信首页」→ 进入「聊天页」→ 打开「朋友圈」,Task 堆栈为:
[微信首页, 聊天页, 朋友圈](栈顶是朋友圈)。 - 跨应用组件组合Task 可以包含不同应用的 Activity。例如:在浏览器中点击一个电话号码,系统会启动拨号应用的拨号 Activity,此时拨号 Activity 会被压入浏览器所在的 Task 堆栈(而非单独创建新 Task),返回时仍回到浏览器页面。
- 与操作系统的关系Task 是 Android 框架层(Framework)的概念,由 ActivityManagerService(AMS) 管理,而非 Linux 内核的进程 / 线程调度。但 Task 中的 Activity 运行在所属应用的进程中,一个 Task 可能对应多个进程(当包含跨应用 Activity 时)。
Task 的关键特性
- 任务亲和性(Task Affinity)
- 每个 Activity 都有
taskAffinity属性(默认与应用包名一致),用于指定它 “倾向” 属于哪个 Task。 - 若 Activity 的
taskAffinity与当前 Task 不同,启动时可能会被放入新 Task(需配合launchMode或Intent标志)。 - 用途:实现 “文档型应用”(如邮件客户端,每封邮件在独立 Task 中打开,可通过多任务切换快速切换邮件)。
- 每个 Activity 都有
- **启动模式(Launch Mode)**启动模式通过
AndroidManifest.xml的launchMode或Intent标志(如FLAG_ACTIVITY_NEW_TASK)控制 Activity 如何进入 Task 堆栈,核心模式包括:- standard(默认):每次启动都创建新实例,压入当前栈顶。
- singleTop:若栈顶已是该 Activity,则复用(调用
onNewIntent),否则创建新实例。 - singleTask:确保栈中只有一个实例,若已存在则将其上方的 Activity 出栈,使其成为栈顶。
- singleInstance:该 Activity 独占一个 Task,且栈中只有它自己(如系统拨号界面)。
- **任务栈管理(通过 Intent 标志)**常用标志可动态改变 Task 行为:
FLAG_ACTIVITY_NEW_TASK:启动新 Task 并将 Activity 放入其中(常用于非 Activity 上下文启动 Activity,如 Service 中)。FLAG_ACTIVITY_CLEAR_TOP:若目标 Activity 已在栈中,则销毁其上方所有 Activity,使其成为栈顶(类似singleTask)。FLAG_ACTIVITY_SINGLE_TOP:等效于singleTop启动模式。
Task 与 Activity 的生命周期关联
Task 堆栈的变化直接触发 Activity 的生命周期:
- 当 Activity 被压入栈顶(前台可见):经历
onCreate() → onStart() → onResume()。 - 当 Activity 被新 Activity 覆盖(仍在栈中,后台):经历
onPause() → onStop()(但未销毁)。 - 当 Activity 从栈顶出栈(用户返回或被销毁):经历
onPause() → onStop() → onDestroy()。
特殊场景:若系统内存不足,可能会销毁后台 Task 中处于 onStop() 状态的 Activity,但保留其状态(通过 onSaveInstanceState 保存);当用户再次返回该 Task 时,系统会重建 Activity 并恢复状态。
Task 的实际应用场景
- 多任务切换用户通过 “最近任务” 按钮切换 Task,每个 Task 对应一个 “应用快照”,切换时显示栈顶 Activity 的界面(如切换微信和浏览器)。
- 清空任务栈退出应用时,可通过
FLAG_ACTIVITY_CLEAR_TASK+FLAG_ACTIVITY_NEW_TASK清除现有 Task 并启动新的根 Activity(避免用户返回旧页面)。 - 保留任务状态系统会在应用退到后台时保留 Task 堆栈(即使进程被销毁),用户再次启动时恢复栈顶 Activity(如退出微信后,再次打开仍显示之前的聊天页)。
总结
- Task 是 Android 系统管理 Activity 的堆栈结构,遵循 LIFO 原则,包含一个或多个 Activity(可跨应用)。
- 核心作用是维护用户交互的连贯性,让用户感觉 “操作是连续的”(如返回键导航)。
- 通过
taskAffinity、启动模式和 Intent 标志,可灵活控制 Activity 在 Task 中的行为,适应不同业务场景(如单实例页面、跨应用协作)。
理解 Task 机制是掌握 Android 组件生命周期和页面导航的关键,也是面试中常考的基础知识点。
先搞懂核心概念:亲和性(Affinity)
要理解 finishAffinity(),必须先明确 “亲和性” 的含义:
亲和性(Affinity) 是 Activity 的一个属性(通过 android:taskAffinity 配置),用于标识 Activity “属于哪个任务栈”。默认情况下,一个 App 内所有 Activity 的亲和性相同(值为 应用包名),因此默认属于同一个 “亲和性组”。
关键规则:
- 同一 App 内的 Activity,默认亲和性相同(共享同一个任务栈);
- 若某 Activity 显式配置
android:taskAffinity="自定义值",则它会属于独立的亲和性组; finishAffinity()仅销毁 “当前 Activity 所在亲和性组” 的所有 Activity,不影响其他亲和性组的 Activity(哪怕在同一个任务栈)。
示例(默认场景):
App 内有 A→B→C→D 四个 Activity(默认亲和性相同),在 D 中调用 finishAffinity(),会销毁 A、B、C、D 所有 Activity,任务栈为空,App 退到桌面。
finishAffinity () 的核心作用与用法
1. 核心作用
- 销毁当前 Activity 及其同亲和性组的所有 Activity,释放相关资源;
- 不影响其他任务栈或其他亲和性组的 Activity;
- 调用后,当前 App 会退到桌面(若所有 Activity 都被销毁),或回到其他亲和性组的 Activity(若存在)。
2. 基本用法(代码调用)
直接在任意 Activity 中调用,无需传入参数:
// 在需要关闭同亲和性组所有 Activity 的地方调用(如“退出登录”按钮点击事件)
buttonLogout.setOnClickListener {
finishAffinity() // 销毁当前 Activity 及同亲和性组的所有 Activity
}
3. 与类似方法的区别(避免混淆)
很多人会把 finishAffinity() 与 finish()、finishAndRemoveTask() 搞混,核心区别如下:
| 方法 | 作用范围 | 适用场景 |
|---|---|---|
finish() | 仅销毁当前 Activity(任务栈中其他 Activity 保留) | 关闭单个页面(如返回上一页) |
finishAffinity() | 销毁当前 Activity + 同亲和性组的所有 Activity | 退出整个 App、退出登录返回桌面 |
finishAndRemoveTask() | 销毁当前 Activity 所在的整个任务栈(不管亲和性) | 关闭整个任务栈(如一次性任务场景) |
示例对比:
- 任务栈:A→B→C→D(同亲和性)
- 调用
D.finish():仅销毁 D,栈变为 A→B→C; - 调用
D.finishAffinity():销毁 A、B、C、D,栈为空; - 调用
D.finishAndRemoveTask():销毁整个任务栈(效果同finishAffinity(),但不区分亲和性)。
- 调用
- 任务栈:A(亲和性:包名)→ B(亲和性:自定义值)→ C(亲和性:包名)
- 调用
C.finishAffinity():仅销毁 A、C(同亲和性),B 保留; - 调用
C.finishAndRemoveTask():销毁整个任务栈(A、B、C 全部销毁)。
- 调用
- Android 任务栈的 “销毁逻辑” 与 “栈位置无关,只与实例引用和亲和性有关”
C.finishAffinity()中B保留的解释如下:- 栈结构是 “逻辑顺序”,不是 “物理屏障”:任务栈的 “栈底→栈顶” 只是 Activity 的 “显示顺序” 和 “返回键顺序”,不是 “销毁顺序的强制限制”—— 系统只要能找到 A 的实例(通过亲和性筛选),就能直接调用
A.finish(),无需先销毁上方的 B; - Activity 实例是独立的:A、B、C 是三个独立的 Activity 实例,它们的生命周期互不绑定(默认启动模式下)。A 的销毁不会触发 B 的销毁,B 的存在也不会阻止 A 的销毁;
finishAffinity()突破了 “栈顶销毁” 的默认规则:默认finish()只能销毁当前 Activity(栈顶),但finishAffinity()是 “批量销毁 API”,它的逻辑是 “按亲和性找实例 → 批量 finish”,天然支持 “跨栈位置销毁”。
- 栈结构是 “逻辑顺序”,不是 “物理屏障”:任务栈的 “栈底→栈顶” 只是 Activity 的 “显示顺序” 和 “返回键顺序”,不是 “销毁顺序的强制限制”—— 系统只要能找到 A 的实例(通过亲和性筛选),就能直接调用
finishAffinity () 的典型使用场景
1. 退出整个 App(最常用)
当用户点击 “退出 App” 按钮时,需要关闭所有已打开的页面,直接退到桌面,此时 finishAffinity() 是最优选择(无需手动遍历任务栈销毁每个 Activity)。
示例(退出 App 按钮):
btnExitApp.setOnClickListener {
finishAffinity() // 关闭所有同亲和性的 Activity
// 可选:配合 System.exit(0) 彻底终止进程(不推荐,可能导致资源释放不完整)
// System.exit(0)
}
2. 退出登录,返回桌面或登录页
用户退出登录后,需要销毁当前 App 内所有已打开的业务页面(如首页、订单页、个人中心),避免退出后仍能通过 “返回键” 回到业务页面。
示例(退出登录流程):
btnLogout.setOnClickListener {
// 1. 清除登录态(如 SharedPreferences、Token)
clearLoginState()
// 2. 销毁所有业务页面(同亲和性组)
finishAffinity()
// 3. 可选:跳转到登录页(若需要用户重新登录)
val intent = Intent(this, LoginActivity::class.java)
// 给登录页设置新的任务栈(避免返回键回到已销毁的页面)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
}
3. 一次性任务场景(如临时操作页面)
某些场景下,App 打开一组临时页面(如 “活动专题页→活动详情页→领奖页”),用户完成操作后(如领奖成功),需要关闭这组页面,回到之前的主页面。此时可给这组临时页面配置相同的自定义亲和性,完成后调用 finishAffinity() 一次性销毁。
示例(临时页面配置亲和性):
- 在 AndroidManifest.xml 中给临时页面配置相同的亲和性:
<!-- 活动专题页 -->
<activity
android:name=".activity.ActivityTopicActivity"
android:taskAffinity="com.xxx.app.temp.activity" /> <!-- 自定义亲和性 -->
<!-- 活动详情页 -->
<activity
android:name=".activity.ActivityDetailActivity"
android:taskAffinity="com.xxx.app.temp.activity" />
<!-- 领奖页 -->
<activity
android:name=".activity.ActivityAwardActivity"
android:taskAffinity="com.xxx.app.temp.activity" />
- 在领奖页完成操作后调用:
// 领奖成功后,销毁所有临时页面(同亲和性)
finishAffinity()
// 回到主页面
startActivity(Intent(this, MainActivity::class.java))
4. 使用注意事项(避坑要点)
API 版本限制:
finishAffinity()是 API 16(Android 4.1)引入的,若需兼容更低版本(如 API 14-15),需用兼容方案:if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { finishAffinity() } else { // 低版本兼容:遍历任务栈销毁所有 Activity(需自己维护 Activity 栈) ActivityManager.finishAllActivities() }亲和性配置影响效果:若 Activity 配置了自定义
taskAffinity,finishAffinity()仅销毁同亲和性的 Activity,需注意配置一致性(避免误销毁或漏销毁)。不终止进程:
finishAffinity()仅销毁 Activity,但 App 进程可能仍存在(Android 系统会管理进程生命周期),这是正常现象(无需手动调用System.exit(0),强制终止进程可能导致资源释放不完整,如数据库连接未关闭)。配合启动模式使用:若退出后需要跳转到登录页,需给登录页设置
FLAG_ACTIVITY_NEW_TASK标记,避免登录页被加入已销毁的任务栈,导致返回键异常。多任务栈场景需谨慎:若 App 存在多个任务栈(如通过
FLAG_ACTIVITY_NEW_TASK启动的独立任务栈),finishAffinity()仅销毁当前任务栈中同亲和性的 Activity,其他任务栈不受影响,需根据需求处理。
示例代码:
public class MyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 关闭当前Activity
finish();
// 关闭所有关联的Activity
finishAffinity();
// 将当前任务放到后台
moveTaskToBack(true);
}
}
通过上述示例和解释,你可以有效地理解和实现各种与Activity相关的功能和交互。
如何正确关闭Activity
正确关闭Activity的方式:
- 使用
finish(): 关闭当前Activity。 - 使用
finishAffinity(): 销毁当前 Activity + 同亲和性组的所有 Activity,退出整个 App、退出登录返回桌面。 - 使用
finishAndRemoveTask(): 销毁当前 Activity 所在的整个任务栈(不管亲和性),关闭整个任务栈(如一次性任务场景)。 - 使用
moveTaskToBack(): 将当前任务放到后台。
TaskAffinity 是干啥用的?
TaskAffinity 主要用于定义 Activity 所属的任务栈。在 Android 系统中,任务栈是用于管理 Activity 的栈结构。
当一个 Activity 被启动时,它会被放置到一个任务栈中。TaskAffinity 属性可以指定 Activity 希望归属的任务栈。默认情况下,一个应用的所有 Activity 都具有相同的 TaskAffinity,也就是应用的包名,这使得它们通常都在同一个任务栈中。然而,通过修改 TaskAffinity 属性,可以改变这种默认行为。
例如,在多任务处理场景下,假设有一个新闻应用和一个视频应用。新闻应用中有多个 Activity 用于展示新闻列表、新闻详情等。如果希望在新闻应用中某个特定的 Activity(比如一个专题新闻 Activity)能够在用户切换任务时,与视频应用的任务栈关联起来,就可以通过设置 TaskAffinity 来实现。这样,当用户从新闻应用切换到视频应用,然后再通过系统的任务切换功能回到这个专题新闻 Activity 时,它会出现在视频应用的任务栈环境中,而不是新闻应用原本的任务栈。
另外,TaskAffinity 在处理启动模式为 singleTask 或 singleInstance 的 Activity 时也很重要。对于 singleTask 的 Activity,系统会根据 TaskAffinity 来查找是否已经存在一个任务栈中有该 Activity 的实例。如果存在,就会将这个任务栈切换到前台,并清除该 Activity 之上的其他 Activity。对于 singleInstance 的 Activity,它会始终在自己独立的任务栈中,这个任务栈的 TaskAffinity 就是该 Activity 所指定的。这有助于更好地管理 Activity 的任务栈结构,提供更灵活的用户体验,比如在不同应用之间共享 Activity 或者在多任务环境下优化 Activity 的展示和切换。
Android 中 finishAffinity () 详解:作用、用法与场景
finishAffinity() 是 Android 提供的 Activity 栈管理方法(API 16+ 引入),核心作用是:结束当前 Activity 以及与它处于同一 “任务栈(Task)” 中且具有相同 “亲和性(Affinity)” 的所有 Activity,最终将这些 Activity 全部从任务栈中移除并销毁。
简单说:它不是只关闭当前 Activity,而是 “一锅端”—— 关闭当前 Activity 所在的 “亲和性组” 内的所有 Activity,通常用于 “退出整个 App” 或 “返回指定页面(如登录页)” 的场景。
finishAffinity() 的核心是 “按亲和性批量销毁 Activity”,是 Android 中高效管理 Activity 栈的关键方法:
- 核心场景:退出 App、退出登录、批量关闭临时页面;
- 优势:无需手动遍历 Activity 栈,代码简洁,系统自动处理销毁逻辑;
- 注意:受亲和性配置影响,需兼容低版本,避免滥用
System.exit(0)。
简单记:需要 “一次性关闭一组相关页面” 时,就用 finishAffinity (),它是比 finish() 更强大的 “批量关闭” 工具,比 finishAndRemoveTask() 更灵活(支持按亲和性筛选)。