跳到主要内容

Activity 生命周期与启动模式

问题

Activity 的生命周期是怎样的?四种启动模式分别适用于什么场景?

答案

1. Activity 生命周期

回调调用时机典型操作
onCreate首次创建初始化布局、ViewModel、数据绑定
onStart变为可见注册监听器
onResume获得焦点恢复动画、开始相机预览
onPause失去焦点暂停动画、释放相机
onStop完全不可见释放资源、保存数据
onDestroy销毁释放所有资源

常见场景的生命周期调用

// 场景 1:A 启动 B
// A: onPause → B: onCreate → onStart → onResume → A: onStop

// 场景 2:从 B 返回 A
// B: onPause → A: onRestart → onStart → onResume → B: onStop → onDestroy

// 场景 3:屏幕旋转(默认)
// onPause → onStop → onSaveInstanceState → onDestroy
// onCreate → onStart → onRestoreInstanceState → onResume

// 场景 4:按 Home 键
// onPause → onStop → (onSaveInstanceState)

// 场景 5:弹出对话框
// 如果是 Dialog Activity → onPause(不会 onStop,因为部分可见)
// 如果是普通 AlertDialog → 不影响生命周期(同一 Activity 内)
onSaveInstanceState 的调用时机
  • Android 9 (API 28) 之前:在 onStop 之前调用,顺序不确定
  • Android 9+ (API 28+):在 onStop 之后调用
  • 不保证一定被调用:用户主动按返回键退出时不会调用(因为是用户意愿销毁)

2. 状态保存与恢复

class MyActivity : AppCompatActivity() {

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("key", "value")
outState.putInt("scroll_position", recyclerView.scrollY)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 恢复状态
savedInstanceState?.let {
val value = it.getString("key")
val scrollPos = it.getInt("scroll_position")
}
}

// 或者在这里恢复(此方法只在有 savedInstanceState 时才调用)
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
val value = savedInstanceState.getString("key")
}
}
现代状态保存方案

onSaveInstanceState 只适合保存少量 UI 状态(Bundle 有 1MB 限制)。大量数据应使用:

  • ViewModel — 配置变更时保留(屏幕旋转)
  • SavedStateHandle — 进程被杀后恢复
  • 持久化存储 — Room/DataStore 保存长期数据

3. 四种启动模式

// AndroidManifest.xml 中声明
<activity
android:name=".DetailActivity"
android:launchMode="singleTop" />

// 或通过 Intent Flag 动态设置
val intent = Intent(this, DetailActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
startActivity(intent)

standard(标准模式,默认)

每次启动都创建新实例,可以有多个相同 Activity 实例在栈中。

启动 A → A → A → A
栈: [A] → [A, A] → [A, A, A]

singleTop(栈顶复用)

如果目标 Activity 已在栈顶,则复用并调用 onNewIntent(),否则创建新实例。

栈: [A, B, C]
启动 C → 复用栈顶 C,调用 C.onNewIntent()
启动 B → 创建新 B,栈变为 [A, B, C, B]

适用场景:搜索页面、通知跳转页面(避免重复创建)

singleTask(栈内复用)

在目标任务栈中查找实例,如果存在则清除其上方所有 Activity 并调用 onNewIntent()

栈: [A, B, C, D]
启动 B(singleTask) → 清除 C、D,栈变为 [A, B],调用 B.onNewIntent()

适用场景:主页面(从任何深层页面返回主页)

singleInstance(单实例)

独占一个任务栈,全局唯一实例。

Task1: [A, B]
启动 C(singleInstance) → Task2: [C](独立任务栈)
C 启动 D → Task1: [A, B, D](D 回到原任务栈)

适用场景:来电界面、全局悬浮窗

4. 常用 Intent Flags

Flag效果
FLAG_ACTIVITY_NEW_TASK在新任务栈中启动
FLAG_ACTIVITY_CLEAR_TOP清除目标 Activity 上方的所有 Activity
FLAG_ACTIVITY_SINGLE_TOP等价于 singleTop
FLAG_ACTIVITY_CLEAR_TASK清除整个任务栈,结合 NEW_TASK 使用
FLAG_ACTIVITY_NO_HISTORY不保留在栈中(离开即销毁)
// 典型组合:重新回到登录页并清空栈
val intent = Intent(this, LoginActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
finish()

5. taskAffinity

<!-- 每个 Activity 可以指定 taskAffinity(默认为包名) -->
<activity
android:name=".SettingsActivity"
android:launchMode="singleTask"
android:taskAffinity="com.example.settings" />

taskAffinity 决定 Activity 归属哪个任务栈。配合 singleTask 使用时,系统会查找具有相同 taskAffinity 的任务栈。


常见面试问题

Q1: A 启动 B,两者的生命周期调用顺序是什么?

答案

A.onPause() → B.onCreate() → B.onStart() → B.onResume() → A.onStop()

关键点:A 的 onPause 先于 B 的 onCreate。所以不要在 onPause 中做耗时操作,否则会延迟新 Activity 的显示。

Q2: onSaveInstanceState 和 ViewModel 的区别?

答案

特性onSaveInstanceStateViewModel
存活范围进程被杀后恢复仅配置变更
数据大小小(Bundle,< 1MB无限制(内存)
数据类型Parcelable/Serializable任意对象
典型用途页面滚动位置、输入文本网络数据、列表数据
恢复时机onCreate/onRestoreInstanceState直接访问

最佳实践:ViewModel + SavedStateHandle 组合使用,兼顾两种场景。

Q3: singleTask 启动模式中,onNewIntent 的调用时机?

答案

当 singleTask Activity 已存在于任务栈中时,系统:

  1. 将该 Activity 上方的所有 Activity 出栈(调用它们的 onDestroy
  2. 调用目标 Activity 的 onNewIntent(intent)
  3. 接着调用 onRestartonStartonResume
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
setIntent(intent) // ⚠️ 必须手动更新 Intent
// 处理新的 Intent 数据
handleIntent(intent)
}

Q4: 如何避免屏幕旋转导致 Activity 重建?

答案

<!-- 方案 1:在 Manifest 中声明处理配置变更 -->
<activity
android:name=".MyActivity"
android:configChanges="orientation|screenSize|keyboardHidden" />
// Activity 不会重建,而是回调 onConfigurationChanged
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// 手动处理配置变化
}

方案 2(推荐):不阻止重建,使用 ViewModel 保存数据,让系统正常重建 Activity。这样能正确加载不同方向的布局资源。

Q5: 什么是 Activity 的透明主题?对生命周期有什么影响?

答案

透明 Activity(Theme.Translucent)启动时,下层 Activity仍然可见,因此下层 Activity 的 onStop 不会被调用,只会调用 onPause

<style name="TransparentTheme" parent="Theme.AppCompat">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>

生命周期:下层 Activity onPause(不调用 onStop),因为它仍然部分可见。

相关链接