Android Activity 生命周期结束流程:从 finish() 到 onDestroy() 的跨进程状态调度

一、引子:从 Activity 生命周期日志说起

在分析 Android Activity 的启动和销毁流程时,经常可以看到两类日志:

  • wm_*:由 system_server 进程打印,反映系统侧 Activity 的生命周期状态调度。

  • wm_on_*:由应用进程打印,反映应用侧生命周期回调的执行情况。

例如,在 Activity 销毁过程中,可能观察到以下日志顺序:

Activity 的销毁并不是由应用进程独立完成的,而是 system_server 与应用进程相互配合的结果。

系统首先决定 Activity 是否需要退出,然后通知应用进程执行对应的生命周期回调。应用进程执行完成后,再将执行结果反馈给系统,由系统决定是否继续推进下一阶段。

因此,本文主要研究以下几个问题:

  1. 应用调用 finish() 后,如何通知 system_server?

  2. system_server 如何维护 Activity 的生命周期状态?

  3. 系统如何通过 ClientTransaction 通知应用执行生命周期?

  4. 为什么 Activity 的销毁需要经过 PAUSING、PAUSED、STOPPING 等状态?

  5. 系统如何确认一个 Activity 已经完成销毁?

本文以一个处于 RESUMED 状态的 Activity 主动调用 finish() 为例进行分析。

二、整体架构:谁负责决策,谁负责执行?

在分析具体源码之前,首先需要理解 Android Activity 生命周期的管理架构。

1. 系统侧决策

system_server 通过 ActivityTaskManagerService、ActivityRecord、ActivityStack 等对象管理 Activity 的状态。

它负责判断:

  • 当前 Activity 处于什么状态?

  • 是否需要先执行 pause?

  • 是否需要等待其他 Activity 恢复?

  • 是否可以立即销毁?

2 . 应用侧执行

应用进程收到命令后,由 ActivityThread 和 TransactionExecutor 执行对应操作,最终调用 Activity 的生命周期方法。

4. 应用反馈系统

应用执行完成后,在需要反馈的阶段通过 Binder 通知 system_server。

三、Activity 的两套状态

理解销毁流程之前,需要区分两个容易混淆的概念。

3.1 系统侧 ActivityRecord 的状态

源码位置:frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java

ActivityRecord 维护了一个 mState 字段,用于记录系统侧 Activity 的生命周期状态。

相关状态包括:INITIALIZING STARTED RESUMED PAUSING PAUSED STOPPING

STOPPED FINISHING DESTROYING DESTROYED

系统通过 setState() 更新状态。

复制代码
prev.setState(PAUSING, "startPausingLocked");

3.2 应用侧 Activity 的生命周期

应用进程维护自己的 Activity 实例,并通过 ActivityThread 执行:

复制代码
onCreate()
onStart()
onResume()
onPause()
onStop()
onDestroy()

系统侧 ActivityRecord.mState 与应用侧生命周期存在对应关系,但两者并不等价,也不一定同时更新。

另外,Activity 不是线程。Activity 是运行在应用进程中的组件对象,生命周期回调通常在应用主线程执行。销毁 Activity 不等于销毁主线程或应用进程。

四、第一阶段:finish() 请求系统结束 Activity

假设当前 Activity 处于 RESUMED 状态,应用主动调用finish();

4.1 应用进程调用 finish()

源码:

复制代码
frameworks/base/core/java/android/app/Activity.java

核心调用:

复制代码
ActivityTaskManager.getService().finishActivity(
    mToken,
    resultCode,
    resultData,
    finishTask
);

这里通过 Binder 跨进程调用 system_server。

其中 mToken 用于标识当前 Activity,使系统能够找到对应的 ActivityRecord。

4.2 system_server 接收请求

源码:

复制代码
ActivityTaskManagerService.java

入口:

复制代码
finishActivity(...)

随后进入:

复制代码
ActivityRecord.finishIfPossible(...)

在这里,系统开始处理 Activity 的结束请求。

其中一个关键操作:

复制代码
makeFinishingLocked();

将 ActivityRecord.finishing 标记为 true。

这里需要区分:

  • finishing = true:表示当前 Activity 已被标记为需要结束。

  • mState = FINISHING:表示 ActivityRecord 进入特定生命周期管理状态。

此处还会打印:

复制代码
EventLogTags.writeWmFinishActivity(...);

对应日志:

复制代码
wm_finish_activity:[0,47338819,278,com.xxxxxx app-request]

这条日志表示系统已经开始处理 Activity 的结束请求,并不意味着 onDestroy() 已经执行。

五、第二阶段:RESUMED → PAUSING → PAUSED

5.1 系统开始执行 pause

当前 Activity 处于 RESUMED 状态,系统需要先让它暂停。

源码:

复制代码
ActivityStack.java

关键函数:

复制代码
startPausingLocked(...)

系统首先执行:

复制代码
prev.setState(PAUSING, "startPausingLocked");

随后打印:

复制代码
wm_pause_activity

然后通过:

复制代码
mAtmService.getLifecycleManager()
    .scheduleTransaction(
        prev.app.getThread(),
        prev.appToken,
        PauseActivityItem.obtain(...)
    );

向应用进程发送 PauseActivityItem。

5.2 应用进程执行 PauseActivityItem

系统侧调用:

复制代码
ClientLifecycleManager.scheduleTransaction()
    ↓
ClientTransaction.schedule()
    ↓ Binder
ApplicationThread.scheduleTransaction()

应用进程收到 Binder 请求后,并不是直接在 Binder 线程执行 onPause()。

而是将事务投递到应用主线程:

复制代码
ApplicationThread.scheduleTransaction()
    ↓
ActivityThread.sendMessage(EXECUTE_TRANSACTION)
    ↓
ActivityThread.H.handleMessage()
    ↓
TransactionExecutor.execute()
    ↓
PauseActivityItem.execute()
    ↓
ActivityThread.handlePauseActivity()
    ↓
Instrumentation.callActivityOnPause()
    ↓
Activity.onPause()

这也说明:

Binder 负责跨进程传递命令,ActivityThread 负责在应用主线程执行生命周期。

5.3 应用执行完成后反馈系统

PauseActivityItem 中除了 execute(),还存在 postExecute()。

在执行完暂停操作后,应用通过:

复制代码
ActivityTaskManager.getService()
    .activityPaused(token);

通知 system_server。

系统接收到回调后,最终进入:

复制代码
ActivityStack.completePauseLocked(...)

更新状态:

复制代码
prev.setState(PAUSED, "completePausedLocked");

至此,系统侧完成:

复制代码
RESUMED → PAUSING → PAUSED

这里体现出一个重要机制:

系统先将 Activity 标记为 PAUSING,再通知应用执行 onPause(),最后根据应用反馈完成 PAUSED 状态的确认。

六、第三阶段:PAUSED → STOPPING

在 completePauseLocked() 中,系统会继续判断 Activity 是否需要结束。

由于前面已经执行:

复制代码
makeFinishingLocked();

因此:

复制代码
prev.finishing == true

系统会进入后续结束处理逻辑。

关键函数:

复制代码
ActivityRecord.completeFinishing(...)

在本文研究的路径中,经过条件判断后进入:

复制代码
addToStopping(...)

将 Activity 加入:

复制代码
ActivityStackSupervisor.mStoppingActivities

并更新系统侧状态:

复制代码
setState(STOPPING, "completeFinishing");

需要注意,这一步主要是系统内部的状态调度。

将 ActivityRecord 设置为 STOPPING,不等于应用已经执行了 onStop()。

这里还有一个重要问题:

为什么系统不在 Activity pause 完成后立即销毁它,而是先加入等待处理的集合?

因为 Activity 的结束还可能受到其他 Activity 的启动、恢复、窗口切换和可见性等条件影响。

系统需要协调这些操作,而不是简单地连续调用所有生命周期方法。

七、第四阶段:STOPPING → FINISHING → DESTROYING

后续在处理等待停止和结束的 Activity 时,会进入:

复制代码
ActivityStackSupervisor.java

关键函数:

复制代码
processStoppingAndFinishingActivities(...)

这里会检查待处理 Activity 的状态以及 finishing 标记。

对于符合条件、需要结束的 Activity,继续执行销毁流程。

关键函数:

复制代码
ActivityRecord.destroyIfPossible(...)

在本文所分析的路径中,系统可能先执行:

复制代码
setState(FINISHING, "destroyIfPossible");

随后进入实际销毁调度。

关键函数:

复制代码
destroyImmediately(...)

这里会打印:

复制代码
EventLogTags.writeWmDestroyActivity(...);

对应日志:wm_destroy_activity

这条日志说明系统开始调度销毁,但并不代表应用已经执行完 onDestroy()。

7.1 系统发送 DestroyActivityItem

如果 Activity 仍关联着有效的应用进程,系统可以通过:

复制代码
mAtmService.getLifecycleManager()
    .scheduleTransaction(
        app.getThread(),
        appToken,
        DestroyActivityItem.obtain(
            finishing,
            configChangeFlags
        )
    );

通知应用执行销毁。

然后系统侧将状态设置为:

复制代码
setState(
    DESTROYING,
    "destroyActivityLocked. finishing and not skipping destroy"
);

此时系统处于等待或处理销毁完成的阶段。

八、第五阶段:应用执行 onDestroy()

应用进程收到 DestroyActivityItem 后,进入:

复制代码
TransactionExecutor.execute()
    ↓
executeLifecycleState()
    ↓
DestroyActivityItem.execute()
    ↓
ActivityThread.handleDestroyActivity()
    ↓
ActivityThread.performDestroyActivity()
    ↓
Instrumentation.callActivityOnDestroy()
    ↓
Activity.onDestroy()

8.1 cycleToPath() 的作用

在执行最终生命周期请求前,TransactionExecutor 可能调用:

复制代码
cycleToPath(
    r,
    lifecycleItem.getTargetState(),
    true,
    transaction
);

它的作用是根据应用侧记录的当前生命周期状态和目标状态,计算需要补齐的中间生命周期操作。

例如,当应用侧尚未执行 onStop(),但需要进入销毁阶段时,执行器可能先补齐停止操作。

  • ActivityRecord.mState:system_server 维护的状态。

  • ActivityClientRecord 的生命周期状态:应用进程用于执行事务的状态。

  • cycleToPath():根据应用侧状态计算生命周期执行路径。

  • 因此,不能直接把系统侧 FINISHING 状态当作 cycleToPath() 的输入状态。

是否需要补齐中间生命周期,还需要结合 TransactionExecutor、TransactionExecutorHelper 和应用侧当前状态具体分析。

8.2 Activity.onDestroy() 执行

最终通过:

复制代码
mInstrumentation.callActivityOnDestroy(r.activity);

调用应用中的:

复制代码
Activity.onDestroy();

至此,应用侧完成对应的销毁回调。

但是,这并不意味着系统侧已经完成 ActivityRecord 的销毁管理。

九、第六阶段:DESTROYING → DESTROYED

对于正常完成的销毁流程,应用还需要向系统反馈。

通过:

复制代码
ActivityTaskManager.getService()
    .activityDestroyed(token);

跨进程通知 system_server。

系统进入:

复制代码
ActivityTaskManagerService.activityDestroyed()
    ↓
ActivityRecord.destroyed(...)
    ↓
removeFromHistory()

在相关清理路径中更新:

复制代码
setState(DESTROYED, "removeFromHistory");

这里需要注意,ActivityRecord 可能随后被移出任务历史并清理关联关系,因此 DESTROYED 不代表系统会长期保留这个 ActivityRecord。

至此,本文所分析的正常 finish 销毁流程完成。

十、总结:Activity 生命周期到底由谁控制?

通过上述分析,可以将整个过程整理为:

复制代码
应用进程                         system_server

Activity.finish()
       ────────────────────────→ finishActivity()
                                  ↓
                               finishIfPossible()
                                  ↓
                               finishing = true
                                  ↓
                               mState = PAUSING
       ←──────────────────────── PauseActivityItem
Activity.onPause()
       ────────────────────────→ activityPaused()
                                  ↓
                               mState = PAUSED
                                  ↓
                               completeFinishing()
                                  ↓
                               mState = STOPPING
                                  ↓
                               等待后续处理条件
                                  ↓
                               destroyIfPossible()
                                  ↓
                               mState = FINISHING
       ←──────────────────────── DestroyActivityItem
应用侧补齐必要生命周期
       ↓
Activity.onDestroy()
       ────────────────────────→ activityDestroyed()
                                  ↓
                               清理 ActivityRecord
                                  ↓
                               DESTROYED

上述是本文研究的其中一条典型路径,并不是所有 Activity 都必须严格经过完全相同的状态顺序。

从架构上看,Android 将 Activity 生命周期的决策权放在 system_server,将实际生命周期回调的执行放在应用进程。

系统通过 ActivityRecord 维护管理状态,通过 ClientTransaction 下发执行命令,应用通过 Binder 回调向系统反馈执行结果。

这套机制使系统能够统一协调多个 Activity、多个 Task 和多个应用进程之间的生命周期切换,而不是由应用自行决定所有状态变化。


相关推荐
Jewel1051 小时前
文字为什么总是不居中!记录一个bug
android·flutter·ios
mmsx2 小时前
Android 并发考古:newThread 与 RxJava 共存,以及 GlobalScope 的三宗罪
android·echarts·rxjava
骑着蜗牛撵大象3272 小时前
大模型 SDK 编译就崩?用 CMakeLists 把 httplib、JSON 与 OpenSSL 装配成可复用的 ChatServer
android·java·c++·http·json·cmake·大模型sdk
海水冷却11 小时前
Android 群组聊天怎么实现:基于 ZIM 的完整落地指南(附可运行 Demo)
android
三少爷的鞋14 小时前
现代 Android 架构指南:不要再主动设计 Callback 接口
android
TimeFine20 小时前
AI 编码工作流的设计
android·ai编程
骑着蜗牛撵大象32720 小时前
C++20 协程 × 屏障:手写一个 co_await 版 Barrier,让等待不再钉死线程
android·c++20·协程·并发编程·co_await·屏障同步
山东孙燕姿1 天前
设备台账重构:从一张表到三层模型
android·java·网络·低代码·重构·rxjava
蒸鱼Yuzheng1 天前
移动端 CPU 调度分析:线程亲和性、大小核迁移与卡顿
android·perfetto·性能分析·线程优化·cpu调度