Android Activity 启动时序与 Task 栈管理
前言
在 Android 开发中,Activity 的启动和销毁顺序是理解应用导航流程、内存管理和用户体验优化的基础。很多开发者会疑惑:启动新界面时,是先 finish 旧 Activity 还是先 start 新 Activity?
答案是:先 start 新的,再 finish 旧的。
这种设计保证了界面切换的连续性,避免出现黑屏或空白。本文将详细讲解 Activity 的启动时序、不同 Task 栈的行为差异,以及常用的 Intent Flag 和启动模式。
一、正常启动时序
1.1 生命周期顺序
当从 HomeActivity 启动 DetailActivity 时,系统严格按照以下顺序回调:
1. DetailActivity.onCreate()
2. DetailActivity.onStart()
3. DetailActivity.onResume()
4. HomeActivity.onPause()
5. HomeActivity.onStop()
6. HomeActivity.onDestroy()
关键点:
- 新 Activity(DetailActivity)的
onResume一定先于 旧 Activity(HomeActivity)的onDestroy - 旧 Activity 先走
onPause,等新 Activity 完全可见后才走onStop - 这个顺序是系统保证的,不会乱
1.2 为什么要这样设计?
如果先 finish 旧的再 start 新的,会出现:
- 短暂的黑屏/空白(旧界面已销毁,新界面还没渲染)
- 用户体验差,感觉卡顿
先启动新界面,等新界面绘制完成后再销毁旧界面,保证视觉连续性。
1.3 代码示例:验证启动顺序
kotlin
class HomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("Lifecycle", "HomeActivity.onCreate")
}
override fun onStart() {
super.onStart()
Log.d("Lifecycle", "HomeActivity.onStart")
}
override fun onResume() {
super.onResume()
Log.d("Lifecycle", "HomeActivity.onResume")
}
override fun onPause() {
super.onPause()
Log.d("Lifecycle", "HomeActivity.onPause")
}
override fun onStop() {
super.onStop()
Log.d("Lifecycle", "HomeActivity.onStop")
}
override fun onDestroy() {
super.onDestroy()
Log.d("Lifecycle", "HomeActivity.onDestroy")
}
}
class DetailActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("Lifecycle", "DetailActivity.onCreate")
}
override fun onStart() {
super.onStart()
Log.d("Lifecycle", "DetailActivity.onStart")
}
override fun onResume() {
super.onResume()
Log.d("Lifecycle", "DetailActivity.onResume")
}
}
Logcat 输出:
D/Lifecycle: HomeActivity.onCreate
D/Lifecycle: HomeActivity.onStart
D/Lifecycle: HomeActivity.onResume
// 用户点击启动详情页
D/Lifecycle: DetailActivity.onCreate
D/Lifecycle: DetailActivity.onStart
D/Lifecycle: DetailActivity.onResume
D/Lifecycle: HomeActivity.onPause
D/Lifecycle: HomeActivity.onStop
D/Lifecycle: HomeActivity.onDestroy
二、Intent Flag 与 Task 栈管理
2.1 FLAG_ACTIVITY_CLEAR_TOP
作用: 目标 Activity 已在栈中时,把它上面所有 Activity 全部 finish 掉,让它重新露出来。
示例:
当前栈: HomeActivity → ListActivity → DetailActivity → SettingsActivity
启动 DetailActivity (CLEAR_TOP)
结果栈: HomeActivity → ListActivity → DetailActivity (SettingsActivity 被 finish,DetailActivity 走 onNewIntent 而非 onCreate)
代码示例:
kotlin
val intent = Intent(this, DetailActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
startActivity(intent)
注意:
- 如果 DetailActivity 不在栈中,就正常创建压栈,没有额外效果
- DetailActivity 走
onNewIntent()而非onCreate(),可以在onNewIntent中处理新数据
2.2 FLAG_ACTIVITY_CLEAR_TASK
作用: 启动新 Activity 前,把整个栈清空,新 Activity 成为栈底唯一的。
示例:
当前栈: HomeActivity → ListActivity → DetailActivity → SettingsActivity
启动 LoginActivity (CLEAR_TASK + NEW_TASK)
结果栈: LoginActivity (前面四个全部 finish)
代码示例:
kotlin
val intent = Intent(this, LoginActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
注意:
- 必须配合
FLAG_ACTIVITY_NEW_TASK一起用,否则无效 - 典型场景:用户登出后跳登录页,防止返回栈残留
2.3 FLAG_ACTIVITY_NEW_TASK
作用: 让目标 Activity 在新任务栈中启动,而不是塞进当前调用者的栈。
两种情况:
-
目标 Activity 还没有对应的任务栈
- 直接创建一个新栈,目标 Activity 作为栈底
-
目标 Activity 已有任务栈
- 把那个已有栈带到前台,目标 Activity 走
onNewIntent,不会新建
- 把那个已有栈带到前台,目标 Activity 走
代码示例:
kotlin
// 从 Service 启动 Activity(必须加 NEW_TASK)
val intent = Intent(this, HomeActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
典型用法:
- 从 Service / BroadcastReceiver 启动 Activity(Android 9+ 强制要求)
- 跨应用启动(比如从你的 App 启动系统设置)
- 配合 CLEAR_TASK 清空当前栈后在新栈启动
2.4 Flag 对比表
| Flag | 清谁 | 目标自己 | 典型场景 |
|---|---|---|---|
| CLEAR_TOP | 目标上面的 | 保留(onNewIntent) | 回到列表页 |
| CLEAR_TASK | 整个栈 | 保留(新建,栈底) | 登出后跳登录页 |
| NEW_TASK | 不清 | 在新栈 | Service 启动 Activity |
三、启动模式(launchMode)
3.1 standard(标准模式)
默认模式。 每次启动都创建新实例,压入当前栈。
xml
<activity android:name=".HomeActivity" />
3.2 singleTop(栈顶复用)
如果目标 Activity 已在栈顶,不创建新实例,走 onNewIntent。
xml
<activity android:name=".HomeActivity" android:launchMode="singleTop" />
示例:
栈: HomeActivity → ListActivity → DetailActivity
启动 DetailActivity (singleTop)
结果: DetailActivity 走 onNewIntent,栈不变
3.3 singleTask(栈内复用)
如果目标 Activity 已在栈中,把它上面所有 Activity finish 掉,走 onNewIntent。如果不在栈中,创建新栈。
xml
<activity android:name=".HomeActivity" android:launchMode="singleTask" />
示例:
栈: HomeActivity → ListActivity → DetailActivity → SettingsActivity
启动 ListActivity (singleTask)
结果: HomeActivity → ListActivity (DetailActivity 和 SettingsActivity 被 finish,ListActivity 走 onNewIntent)
典型场景: 浏览器主界面、应用首页
3.4 singleInstance(单实例独占)
独占一个任务栈,整个系统只有一个实例。其他 Activity 启动时,如果该实例已存在,直接走 onNewIntent。
xml
<activity android:name=".PhoneCallActivity" android:launchMode="singleInstance" />
典型场景: 来电界面、闹钟提醒
3.5 启动模式对比
| 模式 | 是否创建新实例 | 是否新栈 | onNewIntent |
|---|---|---|---|
| standard | 总是创建 | 否 | 不走 |
| singleTop | 栈顶时复用 | 否 | 栈顶时走 |
| singleTask | 栈中存在时复用 | 是 | 走 |
| singleInstance | 总是复用 | 独占栈 | 走 |
四、什么情况下新 Activity 的 onResume 会"慢"?
4.1 正常情况不会慢
系统严格保证:新 Activity 的 onResume 一定先于 旧 Activity 的 onDestroy。
4.2 感知到"慢"的原因
如果用户感觉新 Activity 启动慢(旧 Activity 还停留很久),可能是:
1. 新 Activity onCreate 很重
kotlin
// 错误示例:在 onCreate 中做重活
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 阻塞主线程
val data = database.query("SELECT * FROM huge_table")
val bitmap = BitmapFactory.decodeFile("large_image.png")
setContentView(R.layout.activity_main)
}
优化:
kotlin
// 正确示例:异步加载
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 异步加载数据
lifecycleScope.launch(Dispatchers.IO) {
val data = database.query("SELECT * FROM huge_table")
withContext(Dispatchers.Main) {
updateUI(data)
}
}
}
2. 系统资源紧张
- 内存不足,系统先回收其他进程再调度
- CPU 繁忙,ActivityManagerService 调度延迟
3. 转场动画
kotlin
// 自定义动画时间过长
val options = ActivityOptions.makeCustomAnimation(
this,
R.anim.slow_enter, // 1000ms 动画
R.anim.slow_exit
)
startActivity(intent, options.toBundle())
4. 冷启动 vs 热启动
- 冷启动: 进程不存在,要创建进程 → 加载 dex → Application.onCreate → Activity.onCreate
- 热启动: 进程在后台,直接走 onCreate,快
5. 启动模式影响
singleTask/singleTop走onNewIntent,可能触发额外逻辑CLEAR_TASK要先 finish 整个栈,再创建新 Activity
五、知识点总结
5.1 核心要点
- 启动顺序: 先 start 新的(onCreate → onStart → onResume),再 finish 旧的(onPause → onStop → onDestroy)
- 系统保证: 新 Activity 的
onResume一定先于旧 Activity 的onDestroy - 设计目的: 避免界面切换时出现黑屏/空白,保证用户体验
5.2 Intent Flag
| Flag | 作用 | 必须配合 |
|---|---|---|
| CLEAR_TOP | 清空目标上面的 Activity | 无 |
| CLEAR_TASK | 清空整个栈 | NEW_TASK |
| NEW_TASK | 在新栈启动 | 无(但常配合 CLEAR_TASK) |
5.3 启动模式
| 模式 | 复用规则 | 典型场景 |
|---|---|---|
| standard | 不复用 | 普通页面 |
| singleTop | 栈顶复用 | 通知点击跳转 |
| singleTask | 栈内复用 | 应用首页 |
| singleInstance | 独占栈 | 来电界面 |
5.4 优化建议
- 新 Activity 的
onCreate里别做重活,异步加载数据 - 用
ActivityOptions.makeCustomAnimation控制动画时长 - 预加载:提前创建进程或缓存数据
- 避免在
onCreate中阻塞主线程(数据库查询、网络请求、大图解码)
六、常见问题
Q: 能不能先 finish 旧的再 start 新的?
A: 可以,但不推荐。会出现短暂黑屏,用户体验差。
Q: 从 Service 启动 Activity 为什么要加 NEW_TASK?
A: Android 9+ 强制要求非 Activity Context 启动 Activity 时必须加 NEW_TASK,否则抛异常。
Q: singleTask 和 NEW_TASK + CLEAR_TASK 有什么区别?
A:
singleTask是启动模式,写在 manifest 里,永久生效NEW_TASK + CLEAR_TASK是 Intent flag,写在代码里,一次生效- 效果类似,但 flag 是调用方控制的,不需要目标 Activity 配合
Q: 为什么我的新 Activity onResume 比旧 Activity onDestroy 慢?
A: 不可能。系统严格保证顺序。如果你感觉慢,可能是新 Activity 的 onCreate 太重,或者转场动画时间过长。