android-activity-launch-sequence

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 在新任务栈中启动,而不是塞进当前调用者的栈。

两种情况:

  1. 目标 Activity 还没有对应的任务栈

    • 直接创建一个新栈,目标 Activity 作为栈底
  2. 目标 Activity 已有任务栈

    • 把那个已有栈带到前台,目标 Activity 走 onNewIntent,不会新建

代码示例:

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 核心要点

  1. 启动顺序: 先 start 新的(onCreate → onStart → onResume),再 finish 旧的(onPause → onStop → onDestroy)
  2. 系统保证: 新 Activity 的 onResume 一定先于旧 Activity 的 onDestroy
  3. 设计目的: 避免界面切换时出现黑屏/空白,保证用户体验

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 太重,或者转场动画时间过长。


参考链接

相关推荐
YB13753 小时前
Jetpack Compose 动画rememberInfiniteTransition
android
baidu_247438616 小时前
Android 自定义竖直的虚线
android
传奇开心果编程7 小时前
【Jetpack Compose娓娓道来】 第25课:ViewModel就是UI界面的大脑
android·kotlin·移动开发·jetpack compose·编程语言理论·编程框架·声明式ui
马立杰7 小时前
PHP 未授权 RCE 应急响应:CVE-2026-109618 CVE-2026-109619 排查与加固实战
android·开发语言·php
NamoAmitabha1287 小时前
安装“编译器环境”(Android Studio)--deepseek--Cocos --Creator3.8.8
android·ide·android studio
聚美智数8 小时前
文档转换 API‑文档转 PDF‑PDF 转文档‑异步文档处理‑文件格式迁移‑转换结果回调推送 API 接口介绍
android·java·pdf
booboosui9 小时前
Android 上的无人回合:只放开 2 类工具,宁可空手
android
事圆则缓11 小时前
Android 性能排查实战:怎么看内存抖动、CPU 占用和 FPS
android
mmsx11 小时前
Android 三类「看着无关」的故障:大事务、UI 阻塞与 ViewModel 作用域
android·ui