news 2026/10/8 2:02:13

YCBlogs 之 Activity 完全指南:生命周期、启动模式与任务栈深度解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YCBlogs 之 Activity 完全指南:生命周期、启动模式与任务栈深度解析
  • 教程
  • 技术博客
  • 文档

【免费下载链接】YCBlogs

技术博客笔记大汇总,包括Java基础,线程,并发,数据结构;Android技术博客等等;常用设计模式;常见的算法;网络协议知识点;部分flutter笔记;还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!转载请注明出处,谢谢!

项目地址:https://gitcode.com/gh_mirrors/yc/YCBlogs
点击查看免费下载

导读:本文以 YCBlogs 仓库中 android/01.基础组件/02.Activity基础介绍.md 为核心骨架,系统梳理 Android 四大基础组件之首 —— Activity 的生命周期、异常场景处理、四种启动模式与任务栈机制。通过结合仓库中 03.Activity启动流程.md 的 AOSP 源码调用链分析与 question/android/01.Android之基础组件问题.md 的面试题沉淀,读者将完整掌握:七大生命周期方法的正确使用姿势、屏幕旋转与内存回收等异常场景的保命方案、四种启动模式的选型依据,以及任务栈(Task)交互的底层逻辑,可直接应用于日常开发与面试准备。


一、Activity 生命周期全解析

1.1 七大生命周期方法说明

在正常情况下,一个 Activity 从启动到结束会以如下顺序经历整个生命周期:

  1. onCreate():当 Activity 第一次创建时被调用,是生命周期的第一个方法。在此方法中做初始化工作,比如调用setContentView加载界面布局资源、初始化 Activity 所需的数据。也可以借助 onCreate 的 Bundle 参数来恢复异常情况下 Activity 结束时的状态(详见后文)。
  2. onRestart():表示 Activity 正在重新启动。当 Activity 从不可见重新变为可见状态时被调用,一般由用户行为导致——例如用户按 Home 键切换到桌面或打开另一个新 Activity,之后又回到当前 Activity。
  3. onStart():表示 Activity 正在被启动、即将开始,此时 Activity 已经"出现"但还没有出现在前台,无法与用户交互。可以理解为Activity 已经显示出来,但是我们还看不到。
  4. onResume():表示 Activity已经可见,并且出现在前台并开始活动。需要与 onStart 对比记忆:onStart 时 Activity 还在后台,onResume 时才显示到前台。
  5. onPause():表示 Activity 正在停止,仍可见,正常情况下紧接着 onStop 就会被调用。特殊情况下,如果此时快速回到当前 Activity,onResume 会被调用(极端情况)。onPause 中不能进行耗时操作,会影响到新 Activity 的显示——因为 onPause 必须执行完,新的 Activity 的 onResume 才会执行。
  6. onStop():表示 Activity 即将停止、不可见、位于后台。可以做稍微重量级的回收工作,同样不能太耗时。
  7. onDestroy():表示 Activity 即将销毁,是生命周期最后一个回调,可以做回收工作和最终资源释放。

在平常的开发中,最常用的是onCreate()和onDestroy(),分别做初始化与回收操作。

补充理解(源码视角):为什么 onPause 里不能做耗时操作?结合 03.Activity启动流程.md 中对旧版 AOSP 源码的调用链分析可以更直观地看到:启动一个新 Activity 时,SystemServer 进程会先通过startPausingLocked()让栈顶旧 Activity 执行 onPause,链路为:

ActivityStack.startPausingLocked()→IApplicationThread.schedulePauseActivity()→ActivityThread.H.sendMessage()→handlePauseActivity()→performPauseActivity()→Instrumentation.callActivityOnPause()→Activity.performPause()→Activity.onPause()。

只有当旧 Activity 的 onPause 执行完并通过activityPaused()通知服务端后,AMS 才会继续resumeTopActivitiesLocked()去启动新的 Activity。因此 onPause 中的耗时操作会直接阻塞新页面的展示。

1.2 Activity 三种运行状态

Activity 的运行状态与进程优先级划分一一对应,共三种:

  • ① Resumed(活动状态):又叫 Running 状态。Activity 正在屏幕上显示,并且拥有用户焦点,即用户正在操作的那个界面,优先级最高。
  • ② Paused(暂停状态):比较不常见。Activity 在屏幕上是可见的,但并不是屏幕最前端的那个 Activity。例如另一个非全屏或透明的 Activity 处于 Resumed 状态,没有完全遮盖当前 Activity。
  • ③ Stopped(停止状态):Activity 完全不可见时进入此状态,Activity 仍在后台运行,内存中仍保留 Activity 的状态,并没有完全销毁。例如跳转到另外一个界面后,之前的界面还在后台,按回退按钮还会恢复原来的状态;大部分 App 按 Home 键并不会被关闭,此时就是 Stopped 状态。

1.3 App 切换到后台分析

针对后台切换这一高频场景,原文档给出了三个关键结论:

  • App 切换到后台,当前 Activity 会走 onDestroy 吗?不会。会先后走onPause和onStop方法,Activity 实例与状态仍然保留在任务栈与内存中。
  • 一般在 onStop 方法里做什么?
    • 写轮播图时:在onStop中暂停轮播图无限轮播,在onStart中开启自动无限轮播;
    • 写视频播放器时:当 App 切换到后台,需要在onStop中停止视频播放。
  • 什么情况会导致 App 被杀死?被杀死时会走 onDestroy 吗?系统资源不足会导致 App 意外被杀死。应用只有在进程存活的情况下才会按照正常生命周期执行;如果进程被突然 kill 掉(相当于System.exit(0)),进程被杀死后根本不会走 Activity/Fragment 的生命周期。只有在进程不被 kill 掉的正常情况下,才会执行 onDestroy。

二、特殊情况下的生命周期

2.1 情况一:销毁后重建(横竖屏切换)

在横竖屏切换过程中,Activity 会经历销毁并重建的过程,这种场景应尽量避免。理解该场景需要先掌握两个回调:onSaveInstanceState 和 onRestoreInstanceState。

  • 当 Activity 由于异常情况(非人为)终止时,系统会调用onSaveInstanceState来保存当前 Activity 的状态。该方法的调用在onStop 之前,与 onPause 没有既定的时序关系;它只在 Activity 被异常终止的情况下调用。
  • 当异常终止的 Activity 被重建以后,系统会调用onRestoreInstanceState,并把 Activity 销毁时 onSaveInstanceState 保存的 Bundle 对象同时传递给 onRestoreInstanceState 和 onCreate 方法。
  • 恢复状态可以通过onRestoreInstanceState方法完成,其调用时机在onStart 之后。
  • onCreate 与 onRestoreInstanceState 恢复状态的区别:onRestoreInstanceState 回调时其中的 Bundle 对象非空,不用加非空判断;而 onCreate 中的 savedInstanceState 可能为 null,需要非空判断。官方建议优先使用 onRestoreInstanceState。

2.2 情况二:不销毁,通过配置属性接管

比如视频播放器经常涉及屏幕旋转场景,可以通过在 AndroidManifest 的 Activity 中指定如下属性,避免横竖屏切换时 Activity 被销毁重建:

<activity android:name=".activity.VideoDetailActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:screenOrientation="portrait"/>

配置了configChanges后,旋转屏幕不再销毁重建 Activity,而是回调下面的方法:

// 重写旋转时方法,不销毁 activity @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); }

实践要点:configChanges需要按需声明。若只声明orientation,在 Android 3.2(API 13)及以上系统上,旋转屏幕仍可能重建 Activity——因为系统还会检测screenSize的变化,所以通常需要同时声明orientation|screenSize(原文档中同时包含了keyboardHidden,用于避免键盘隐藏状态变化触发重建)。如果业务确实需要响应配置改变,就在声明 configChanges 后重写onConfigurationChanged自行处理。

2.3 情况三:内存不足导致低优先级 Activity 被杀死

Activity 优先级划分与 1.2 节三种运行状态对应:

  1. 前台 Activity——正在和用户交互的 Activity,优先级最高;
  2. 可见但非前台 Activity——比如 Activity 中弹出了一个对话框,导致 Activity 可见但位于后台无法与用户交互;
  3. 后台 Activity——已经被暂停的 Activity,比如执行了 onStop,优先级最低。

当系统内存不足时,会按照上述优先级从低到高去杀死目标 Activity 所在的进程。这种情况下数据的保存与恢复过程与横竖屏切换一致,生命周期情况也一样——即先回调onSaveInstanceState保存状态,重建后通过onRestoreInstanceState/onCreate恢复。

如何判断 Activity 的优先级?除了栈顶的 Activity,其他 Activity 都有可能在内存不足时被系统回收;Activity 越处于栈底,被回收的可能性越大。如果有多个后台进程,系统在选择杀死的目标时,采用最近最少使用算法(LRU)。


三、Activity 启动模式与任务栈

3.1 启动模式的类别与结构

Android 提供了四种 Activity 启动方式:

模式名称核心行为
standard标准模式每次启动都创建新实例
singleTop栈顶复用模式栈顶已存在时复用并回调 onNewIntent
singleTask栈内复用模式栈内存在即复用并回调 onNewIntent
singleInstance单例模式独占一个任务栈,全局唯一实例

Activity 的管理采用**任务栈(Task)**形式,任务栈采用"后进先出"(LIFO)的栈结构。

任务栈(Task)的本质:Google 对 Task 的定义是——Task 实际上是一个 Activity 栈,通常用户感知的一个 Application 就是一个 Task。从这个定义看,Task 与 Service 或其他组件没有任何联系,它只是针对 Activity 而言的。可通过getTaskId()获取任务栈的 ID:如果前面的任务栈已经清空,新开的任务栈 ID 会自动 +1 递增。

3.2 standard 标准模式

每启动一次 Activity,就会创建一个新的 Activity 实例并置于栈顶。谁启动了这个 Activity,那么这个 Activity 就运行在启动它的那个 Activity 所在的栈中。例如 Activity A 启动 Activity B,则会在 A 所在的栈顶压入一个新的 Activity。

特殊场景:如果在 Service 或 Application 中启动一个 Activity(它们没有所谓的任务栈),可以使用标记位 Flag 解决——为待启动的 Activity 指定FLAG_ACTIVITY_NEW_TASK标记位,创建一个新栈。

应用场景与跨进程行为:绝大多数 Activity 使用此模式。如果以 standard 方式启动的 Activity 被跨进程调用:

  • 在Android 5.0 之前,新启动的 Activity 实例会放入发送 Intent 的 Task 的栈顶,尽管它们属于不同的程序,这种设计看起来不太合理;
  • Android 5.0 及之后,上述情景会创建一个新的 Task,新启动的 Activity 放入刚创建的 Task 中,更加合理。

3.3 singleTop 栈顶复用模式

如果需要新建的 Activity 位于任务栈栈顶,那么该 Activity 的实例不会重建,而是重用栈顶实例,并回调onNewIntent:

@Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); }

由于不会重建 Activity 实例,不会回调其他生命周期方法。如果栈顶不是新建的 Activity,就会创建该 Activity 的新实例并放入栈顶。

应用场景:

  • 通知栏点击收到的通知需要启动一个 Activity,该 Activity 可用 singleTop,否则每次点击都会新建一个 Activity 实例;
  • 解决"连续快速点击启动两个 Activity"的 bug:如果待启动的 Activity 使用 singleTop 模式,可以避免重复创建;
  • 与 standard 相同:如果是外部程序启动 singleTop 的 Activity,Android 5.0 之前新创建的 Activity 位于调用者的 Task 中,5.0 及以后会放入新的 Task 中。

3.4 singleTask 栈内复用模式

该模式是一种单例模式——一个栈内只有一个该 Activity 实例。singleTask 可以与taskAffinity配合使用,指定开启的 Activity 加入到哪个栈中:

<activity android:name=".Activity1" android:launchMode="singleTask" android:taskAffinity="com.yc.task" android:label="@string/app_name"> </activity>

关于 taskAffinity 的值:每个 Activity 都有 taskAffinity 属性,指出它希望进入的 Task。如果 Activity 没有显式指明 taskAffinity,该属性就等于 Application 指明的 taskAffinity;如果 Application 也没有指明,那么该值就等于包名。这个属性也可以不设置。

执行逻辑:

  • 如果 Activity 指定的栈不存在,则创建一个栈,并把创建的 Activity 压入栈内;
  • 如果 Activity 指定的栈存在:
    • 栈中没有该 Activity 实例 → 创建 Activity 并压入栈顶;
    • 栈中有该 Activity 实例 →把该 Activity 实例之上的 Activity 全部杀死清除出栈,重用并让该实例处于栈顶,然后调用onNewIntent()方法。

应用场景:

  • 大多数 App 的主页。当我们在主界面点击回退按钮时通常就是退出应用。第一次进入主界面后主界面位于栈底,之后无论打开了多少个 Activity,再次回到主界面时,都应该将主界面之上所有 Activity 移除、让主界面处于栈顶,而不是往栈顶新加一个主界面实例——这样能保证退出应用时所有 Activity 都被销毁。
  • 跨应用 Intent 传递时,如果系统中不存在 singleTask Activity 的实例,那么将创建一个新的 Task,再创建 singleTask Activity 实例放入新 Task 中。
  • 典型案例:浏览器主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。

3.5 singleInstance 单例模式

作为栈内复用模式(singleTask)的加强版:打开该 Activity 时,直接创建一个新的任务栈,并创建该 Activity 实例放入新栈中。一旦该模式的 Activity 实例已经存在于某个栈中,任何应用再激活该 Activity 时都会重用该栈中的实例。

应用场景:呼叫来电界面。这种模式的使用情况比较罕见,在 Launcher 中可能使用。只有确定需要使 Activity 只有一个实例时才考虑使用,建议谨慎使用。

3.6 Activity 的 Flags

Activity 的 Flags 很多,这里介绍几个常用的、用于设定 Activity 启动模式的 Flag,可以在启动 Activity 时通过 Intent 的addFlags()方法设置:

  • ① FLAG_ACTIVITY_NEW_TASK:其效果与指定 Activity 为 singleTask 模式一致;
  • ② FLAG_ACTIVITY_SINGLE_TOP:其效果与指定 Activity 为 singleTop 模式一致;
  • ③ FLAG_ACTIVITY_CLEAR_TOP:具有此标记位的 Activity 启动时,在同一个任务栈中所有位于它上面的 Activity 都要出栈。
    • 如果与 singleTask 模式一起出现:被启动的 Activity 已存在栈中时,清除其之上的 Activity,并调用该 Activity 的 onNewIntent 方法;
    • 如果被启动的 Activity 采用 standard 模式:该 Activity 连同之上的所有 Activity 出栈,然后创建新的 Activity 实例并压入栈中。

同一程序不同的 Activity 是否可以放在不同的 Task 任务栈中?可以:

  • 使用 singleInstance 启动模式,Activity 可以运行在另外的单独任务栈中,该 Activity 在内存中只有一份,不会重复开启;
  • 也可以在激活一个新的 Activity 时,给 Intent 设置 Flag——添加FLAG_ACTIVITY_NEW_TASK,被激活的 Activity 就会在新的 Task 栈中。

3.7 特殊情况栈交互:前台栈与后台栈

假如目前有两个任务栈:前台任务栈为 AB,后台任务栈为 CD(假设 CD 的启动模式均为 singleTask):

  • 请求启动 D:后台任务栈整个被切换到前台,此时整个后退列表变成ABCD。当用户按 back 返回时,列表中的 Activity 会一一出栈。
  • 请求启动 C:情况又不一样——调用 singleTask 模式的后台任务栈中的 Activity,会把整个栈的 Activity 压入当前栈的栈顶;同时 singleTask 具有 clearTop 特性,会把该 Activity 之上的栈内 Activity 清除。

小结:singleTask 模式的"全局唯一 + clearTop 清栈"特性,是跨 Task 交互时栈结构发生变化的核心原因。


四、Activity 异常情况:状态保存与恢复

4.1 异常生命周期

异常条件会调用什么方法

当非人为终止 Activity 时(系统配置发生改变导致 Activity 被杀死并重新创建、资源内存不足导致低优先级 Activity 被杀死),会调用onSaveInstanceState()保存状态。该方法调用在onStop 之前,但与 onPause 没有时序关系。

onSaveInstanceState() 与 onPause() 的区别:onSaveInstanceState 适用于临时性状态的保存;onPause 适用于数据的持久化保存。

当异常崩溃后 App 又重启,会走onRestoreInstanceState()方法,可以在此方法中取出 onSaveInstanceState 保存的状态数据。

什么时候会引起异常生命周期
  • 资源相关的系统配置发生改变或资源不足:例如屏幕旋转,当前 Activity 会销毁,并在 onStop 之前回调 onSaveInstanceState 保存数据;重新创建 Activity 时在 onStart 之后回调 onRestoreInstanceState。其中 Bundle 数据会传到 onCreate(不一定有数据)和 onRestoreInstanceState(一定有数据)。
  • 异常导致 App 崩溃但进程没有被完全杀死,重启回到 Activity 页面,也会引起异常生命周期。

4.2 后台 Activity 被异常回收

回收后怎么办

Activity 提供了onSaveInstanceState()回调方法,该方法保证一定在 Activity 被回收之前调用,可解决 Activity 被回收时临时数据得不到保存的问题。onSaveInstanceState 携带一个 Bundle 类型参数,Bundle 提供了一系列保存数据的方法,例如putString()保存字符串、putInt()保存整型数据。每个保存方法需要传入两个参数:第一个是键(用于从 Bundle 中取值),第二个是真正要保存的内容。

onSaveInstanceState() 和 onRestoreInstanceState() 的特点

这两个方法并不是生命周期方法,不同于 onCreate、onPause 等生命周期方法,它们并不一定会被触发。示例代码如下:

// 保存数据 @Override protected void onSaveInstanceState(Bundle outBundle) { super.onSaveInstanceState(outBundle); outBundle.putBoolean("Change", mChange); } // 取出数据 @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); mChange = savedInstanceState.getBoolean("Change"); } // 或者在 onCreate 方法取数据也可以 // onCreate() 方法其实也有一个 Bundle 类型的参数。这个参数在一般情况下都是 null, // 但是当 Activity 被系统回收之前通过 onSaveInstanceState() 方法保存过数据的话, // 这个参数就会带有之前所保存的全部数据 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { String data = savedInstanceState.getString("data"); } }
什么时候会触发这两个方法
  • 当应用遇到意外情况(如内存不足、用户直接按 Home 键)由系统销毁一个 Activity 时,onSaveInstanceState()会被调用;
  • 当用户主动销毁一个 Activity 时(例如在应用中按返回键),onSaveInstanceState()不会被调用;
  • 除非 Activity 是被用户主动销毁的,否则 onSaveInstanceState 只适合用于保存临时性状态,onPause 适合用于数据持久化保存。
onSaveInstanceState() 被执行的场景

系统不知道按下 HOME 后要运行多少其他的程序,自然也不知道 Activity A 是否会被销毁,因此系统都会调用onSaveInstanceState(),让用户有机会保存某些非永久性数据。以下场景都遵循该原则:

  • 按下 Home 键时;
  • 长按 Home 键选择运行其他程序时;
  • 锁屏时;
  • 从 Activity A 中启动一个新的 Activity 时;
  • 屏幕方向切换时。

4.3 屏幕旋转时的生命周期对比

屏幕旋转时如果不做任何处理,Activity 会经历销毁到重建的过程,一般这种效果都不是想要的,视频播放器就经常涉及屏幕旋转场景。

第一种情况:当前 Activity 不销毁。设置android:configChanges="orientation|keyboardHidden|screenSize"后,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法:

<activity android:name=".activity.VideoDetailActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:screenOrientation="portrait"/>
// 重写旋转时方法,不销毁 activity @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); }

第二种情况:销毁当前 Activity 后重建(应尽量避免)。不设置android:configChanges时,切屏会重新调用各个生命周期——默认首先销毁当前 Activity,然后重新加载。

4.4 两个经典问题:如何避免重建、如何恢复状态

问题一:如何避免配置改变时 Activity 重建?在 AndroidManifest.xml 中对应的 Activity 设置android:configChanges="orientation|screenSize"。此时再次旋转屏幕,该 Activity 不会被系统杀死和重建,只会调用onConfigurationChanged。因此,当程序需要响应配置改变时,指定 configChanges 属性、重写 onConfigurationChanged 方法即可。典型使用场景:视频播放器横竖屏切换播放视频。

问题二:优先级低的 Activity 在内存不足被回收后,怎样恢复到销毁前状态?优先级低的 Activity 在内存不足被回收后,重新打开会引发 Activity 重建。Activity 被重新创建时会调用onRestoreInstanceState(在 onStart 之后),并将 onSaveInstanceState 保存的 Bundle 对象作为参数传到 onRestoreInstanceState 与 onCreate 方法。因此可通过这两个方法判断 Activity 是否被重建并取出数据恢复。注意:在 onCreate 取出数据时一定要先判断 savedInstanceState 是否为空。


五、启动模式与生命周期的源码级印证

5.1 启动流程中的关键调用链

结合仓库 03.Activity启动流程.md,可以印证本文第一部分"为什么 onPause 先于新 Activity 的 onCreate/onResume"的结论。完整启动流程(基于旧版 AOSP 源码)大致为:

MyActivity.startActivity() → Activity.startActivity() → Activity.startActivityForResult() → Instrumentation.execStartActivity() → ActivityManagerNative.getDefault().startActivityAsUser() → ActivityManagerService.startActivity() → ActivityStackSupervisor.startActivityMayWait() → ... → startPausingLocked() // 让旧 Activity 执行 onPause → IApplicationThread.schedulePauseActivity() → ActivityThread.handlePauseActivity() → performPauseActivity() → callActivityOnPause() → onPause() → 应用进程通知 AMS activityPaused() → resumeTopActivitiesLocked() → startSpecificActivityLocked() → 进程未启动则 startProcessLocked() 启动进程 → attachApplicationLocked() → realStartActivityLocked() → scheduleLaunchActivity() → handleLaunchActivity() → performLaunchActivity() → callActivityOnCreate() → onCreate() → activity.performStart() → onStart() → handleResumeActivity() → performResumeActivity() → onResume()

几个值得记忆的源码细节(引用自 03.Activity启动流程.md):

  • Activity 的创建是反射完成的:performLaunchActivity中通过mInstrumentation.newActivity(cl, component.getClassName(), r.intent)以反射机制创建 Activity 实例;
  • startActivity 内部就是 startActivityForResult:startActivity(intent)最终调用startActivityForResult(intent, -1),且只有requestCode >= 0时onActivityResult才会被回调——这解释了为什么普通 startActivity 不会收到结果;
  • Instrumentation 的角色:Instrumentation 是 Activity 在应用进程端启动的实际操作类,应用进程端与 SystemServer 服务进程端相互配合,最终完成 Activity 在系统中的启动;
  • Binder 双向通信:通过ActivityManagerNative → ActivityManagerService实现应用进程到 SystemServer 进程的通信;通过ApplicationThread → IApplicationThread实现 SystemServer 进程到应用进程的通信。

5.2 从 Launcher 到启动模式的系统背景

结合 03.Activity启动流程.md 中 Launcher 的启动分析:

  • Launcher 中点击应用图标执行startActivity(intent),Intent 通过setClassName(packageName, className)组装;
  • Launcher 使用隐式启动的原因:Launcher 与应用不在同一进程,无法引用到目标 Activity 的字节码,只能通过 Intent 携带的组件信息交给系统解析;
  • 应用图标与 Launcher Activity 的关联:系统启动时启动 PackageManagerService(包管理服务),解析应用的 AndroidManifest.xml 得到所有组件信息;Launcher 查询包含MAIN + LAUNCHERintent-filter 的 Activity 并为每个创建一个快捷图标:
<intent-filter> <action android:text="android.intent.action.MAIN" /> <category android:text="android.intent.category.LAUNCHER" /> </intent-filter>

而整个应用进程的诞生,可以追溯到 00.App启动流程梳理.md 中的进程链:init 进程 → Zygote 进程 → SystemServer 进程 → 各种应用进程。Activity 的启动请求最终交由运行在 SystemServer 进程中的 ActivityManagerService 统筹调度。


六、面试要点速查

以下要点综合了原文档末尾的问答总结与仓库 question/android/01.Android之基础组件问题.md 中沉淀的面试题,是 Activity 主题的高频考点:

6.1 四种启动模式一句话概括

  • standard 标准模式:每次启动一个 Activity 就会创建一个新的实例;
  • singleTop 栈顶复用模式:如果新 Activity 已位于任务栈栈顶,不会重新创建,并回调onNewIntent(intent);
  • singleTask 栈内复用模式:只要该 Activity 在一个任务栈中存在,都不会重新创建,并回调onNewIntent(intent);如果不存在,系统先寻找是否存在需要的栈——不存在则创建新任务栈并放入,存在则创建到已存在的栈中;
  • singleInstance 单实例模式:具有此模式的 Activity 只能单独位于一个任务栈中,且此任务栈中只有唯一一个实例。

6.2 singleTop 和 singleTask 的区别及应用场景

对比项singleTopsingleTask
实例数量同个 Activity 实例在栈中可以有多个,可能重复创建同个 Activity 实例在栈中只有一个,不存在重复创建
任务栈默认进入启动它所属的任务栈,不引起任务栈变更可通过android:taskAffinity设定需要的任务栈,可能引起任务栈变更
典型场景防止快速点击时多次 startActivity(通知栏跳转、快速点击防抖)主页、登录页

onNewIntent 回调时机:

  • singleTop:新 Activity 已位于任务栈栈顶,不会重新创建,回调onNewIntent(intent);
  • singleTask:只要该 Activity 在一个任务栈中存在,都不会重新创建,回调onNewIntent(intent)。

6.3 任务栈的作用

任务栈存放 Activity 的引用,Activity 不同的启动模式对应不同的任务栈存放方式;可通过getTaskId()获取任务栈 ID,前面的任务栈已清空时新开任务栈 ID 自动 +1 递增。Task 是 Google 定义的 Activity 栈,用户感知的一个 Application 就是一个 Task,Task 与 Service 等其他组件没有联系,只针对 Activity 而言。

6.4 App 切后台与进程被杀

  • App 切后台:Activity 走onPause→onStop,不会走 onDestroy;
  • onStop 中可做轮播暂停、视频停止播放等操作;
  • 系统资源不足导致进程被 kill(相当于System.exit(0)),不会走任何 Activity/Fragment 生命周期方法;
  • Activity 被回收恢复:覆写onSaveInstanceState()将状态数据存入 Bundle,重建时该 Bundle 作为参数传给 onCreate,取出数据恢复到被销毁前的状态。

6.5 异常场景必背结论

  • 屏幕旋转(未处理 configChanges):先onSaveInstanceState(onStop 之前)保存数据,销毁重建后onRestoreInstanceState(onStart 之后)恢复;
  • Bundle 数据传往两处:onCreate(不一定有数据,需判空)和 onRestoreInstanceState(一定有数据,无需判空),建议优先用 onRestoreInstanceState;
  • 避免重建:android:configChanges="orientation|screenSize"+ 重写onConfigurationChanged;
  • 进程被回收的优先级判断:除栈顶外都可能被回收,越靠近栈底回收可能性越大,多后台进程时采用 LRU 算法选择被杀目标。

总结

Activity 作为 Android 四大组件之首,其生命周期、启动模式与任务栈机制构成了应用页面管理的基础。本文以 02.Activity基础介绍.md 为骨架,完整覆盖了七大生命周期方法、三种运行状态、后台切换行为、屏幕旋转与内存回收等异常场景、四种启动模式及其 Flags、前后台任务栈交互,并借助仓库内 03.Activity启动流程.md 的 AOSP 调用链分析印证了"onPause 先于新页面显示"等关键结论。开发者在实践中应记住三条主线:onPause/onStop 不做耗时操作、异常场景用 onSaveInstanceState/onRestoreInstanceState 保存恢复临时状态、根据页面角色(主页/通知页/来电页)选择正确的启动模式。

如需进一步深入,可继续阅读仓库内关联文档:03.Activity启动流程.md(启动调用链源码分析)、00.App启动流程梳理.md(Zygote 与 SystemServer 进程)、01.ActivityThread分析.md(应用进程主线程入口)、question/android/01.Android之基础组件问题.md(更多基础组件面试题)。

  • 教程
  • 技术博客
  • 文档

【免费下载链接】YCBlogs

技术博客笔记大汇总,包括Java基础,线程,并发,数据结构;Android技术博客等等;常用设计模式;常见的算法;网络协议知识点;部分flutter笔记;还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!转载请注明出处,谢谢!

项目地址:https://gitcode.com/gh_mirrors/yc/YCBlogs
点击查看免费下载
上一篇:NVIDIA NeMo Checkpoint 格式全解析:.nemo、.ckpt、.safetensors 与分布式 Checkpoint 的保存、恢复与实战应用
下一篇:TiKV HTTP API 指南:基于 Status Server 的 CPU / Heap Profiling 与符号解析实战

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 2:01:19

Metro UI CSS Shortcut 组件实战指南:构建桌面式快捷启动图标

前端UI组件 【免费下载链接】Metro-UI-CSS A progressive front-end framework for creating high-performance responsive reactive web applications! 项目地址&#xff1a; https://gitcode.com/gh_mirrors/me/Metro-UI-CSS 点击查看 免费下载 导读 Shortcut&#xff08;快…

作者头像 李华
网站建设 2026/10/8 2:01:12

苹果上架被拒 5.6 怎么办

苹果上架被拒 5.6 怎么办&#xff1f;从代码、UI、环境三个方向解决最近我们在处理苹果 App Store 上架时&#xff0c;遇到 5.6 的情况明显多了。很多开发者看到 Guideline 5.6&#xff0c;第一反应是账号是不是出问题了&#xff0c;甚至觉得这个开发者账号已经不能用了。其实不…

作者头像 李华
网站建设 2026/10/8 1:59:41

Superpowers本地AI编程工作流:Claude Code+Antigravity+Codex+Cursor实战指南

1. 项目概述&#xff1a;Superpowers 不是超能力&#xff0c;而是开发者工作流的“肌肉增强器”最近在好几个技术群和开源社区里&#xff0c;频繁看到“superpowers”这个词被反复提起——不是漫威电影里的变种人设定&#xff0c;也不是某个新出的玄学工具&#xff0c;而是指代…

作者头像 李华
网站建设 2026/10/8 1:56:50

谢希仁计算机网络第八版课后答案PDF:高效复习与避坑指南

简介&#xff1a;这份PDF资料面向正在学习《计算机网络》课程的高校学生与考研备考者&#xff0c;针对谢希仁教材第八版与第七版课后习题提供配套参考答案&#xff0c;帮助读者在完成作业、复习考点和自测理解程度时快速核对思路。资源包内共1个PDF文件&#xff0c;大小约1.63M…

作者头像 李华
网站建设 2026/10/8 1:56:23

LangChain 引入和快速上手

&#x1f4d1; 目录 一、复杂场景下&#xff0c;LLM 嵌入应用面临的问题二、LangChain 介绍 LangChain 的核心设计理念LangChain 的标准模块与接口 三、6 步构建第一个 LangChain 应用四、引出 LangChain 相关概念 Runnable 接口LangChain Expression Language&#xff08;LCE…

作者头像 李华