Activity 系统调度与生命周期源码探秘
当你使用 startActivity() 启动一个页面时,如果你认为系统只是简单地反射 new 了一个 Activity 对象并回调了 onCreate(),那就大错特错了。
Activity 是 App 进程与 system_server 进程之间交互的契约窗口。理解生命周期,本质上是理解系统核心服务(ATMS)如何跨进程把控 App 的内存状态、UI 可见性和输入焦点。
本文将摒弃简单的 API 罗列,带你深入 AOSP 源码,拆解一个 Activity 实例从无到有、从跨进程深渊呼叫再到 App 底层最终触发 onCreate() 的硬核全链路流转过程。
1. 设计哲学:控制反转与提线木偶
为什么 Android 不像普通 C++ 游戏引擎那样,给你一个 main() 函数让你用 while(true) 自己去处理循环渲染和按键输入?
因为移动操作系统的内存和焦点资源极其珍贵。系统必须掌握绝对的生杀大权(比如随时在后台强杀应用回收内存,或者在电话拨入时强制剥夺屏幕焦点)。 为了实现这种被动式的强管控,Android 采用了**控制反转(IoC)**的架构。
你可以把 Activity 的生命周期想象成一个与现实极度拟真的大型戏剧班子与院线剧场监管模型:
- 高高在上的剧院大总管:
ActivityTaskManagerService(ATMS,驻扎在系统内核级别进程system_server中,拥有操纵所有院线的全局帝王视角)。 - 长途跨区沟通热线:
BinderIPC 跨进程通信机制。 - 驻扎各个剧场后台的发薪中心调度员:
ActivityThread(运行在你 App 进程的主线程中,负责实际强硬接听总管电话跑腿下令)。 - 演员自己:开发者重写的
onCreate、onResume等生命周期仅仅是这些可怜的演员按照调度员强制指令被迫摆出的“穿衣、出场固定姿势”。
2. 宏观架构:跨进程调度的类图全景
在深入单步源码之前,我们先从类图视角俯瞰整个“剧院大总管”系统。system_server 进程与我们的 App 进程在架构上有着极其严格的界限映射:
┌──────────────────────────────────────────────────────────────────────────┐
│ 系统进程 (system_server) │
└──────────────────────────────────────────────────────────────────────────┘
[ActivityTaskManagerService] (ATMS总管, 供剧团跨界打请求电话)
│
│ (1. 委派查档和接单机制)
▼
[ActivityStarter] (审查科:查验 Manifest 上岗证,处理 LaunchMode)
│
│ (2. 触发栈顶抢位焦点夺取与流转)
▼
[RootWindowContainer] (院线监督:负责统排所有显示舞台档期的最高统治者)
│
│ (3. 排列任务集合)
▼
[Task] (舞台演出区:也就是俗称的 Activity 任务栈)
│
│ (4. 创建档案记录卡,挂靠在系统侧)
▼
[ActivityRecord] (演员电子档案:里面持有对应 App 内该演员对应的联络证明)
│
│ (5. 跨越进程空间边界, 发射包装好的 Transaction 绝密剧本信函)
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈│┈┈┈┈┈┈┈┈ Binder IPC 通信线 (深海内核空间) ┈┈┈┈┈┈┈┈┈┈┈┈┈┈
│
┌─────────────────│────────────────────────────────────────────────────────┐
│ ▼ 应用进程 (App Process) │
└─────────────────│────────────────────────────────────────────────────────┘
[ApplicationThread] (剧团客服部:这也是 Binder 的服务端接听存根,专职接听系统宣判)
│
│ (6. 前台通过 Handler 飞鸽传书,发去大本营队列)
▼
[ActivityThread] (剧团后台发薪室/引擎室! 分发 performLaunchActivity 等最终指令)
│
│ (7. 唤起剧团的大管家经纪人)
▼
[Instrumentation] (应用的专属代理人, 用于全方位拦截辅助造人和通报)
│
│ (8. 通过 ClassLoader.loadClass().newInstance 反射无参构造)
▼
[Activity] (你的组件代码类,此时这里刚刚通过捏泥巴捏出了一个死人)
│
│ (9. 调用强大的神奇方法 attach() 将各种经络系统连通)
▼
[ContextImpl] & [PhoneWindow] (给死人赋予一套华美的登场戏服,使其接引天地获得查询数据库资源及作画的权力)
如图所示,系统内部并没有什么直接控制的 Activity 肉身概念,它的化身是被系统死死捏在手里的 ActivityRecord 头衔。system_server 进程借由它通过 IApplicationThread 这根丝线,在千里之外发号施令。
3. 全链路源码树:一场跨越双进程的“戏剧首演”
一次平凡的 startActivity,底层的详尽调用轨迹穿越了宏观的跨进程天堑。让我们以 “演员申请上台首演” 的比喻,伴随极简版本的核心源码来深入拆解其间共计几十个惊心动魄且带有严重线程挂起设防的节点步骤!
阶段一:演员要求登台首演 (App 进程发起越狱级请求)
起步必须从你在 App 侧的调令指令算起,必须要借助剧团的经纪代理人 Instrumentation 利用专线强行送给剧团。
【执行纪要】:
Activity.startActivity(Intent)Activity.startActivityForResult()Instrumentation.execStartActivity(): 经纪人拿着专线(获取到了 ATMS Binder Proxy)。ActivityTaskManager.getService().startActivity(...): 信号破壁跨越 IPC 通信墙。
【源码洞破与架构本质】:
// fwks/.../app/Instrumentation.java
public ActivityResult execStartActivity(...) {
try {
// ⚡️ 【长途起点】:经纪人主动拨通院线总管 (ATMS) 的绝密电话
int result = ActivityTaskManager.getService().startActivity(...);
// 如果剧院一查数据库发现你涉嫌“偷渡没有办理官方演出执照(没注册 Manifest)”
// ATMS 马上返回封杀码。在这里立刻逼迫该应用执行饮弹了断产生致命抛异常!
checkStartActivityResult(result, intent);
} catch (RemoteException e) { ... }
}
【为何这么设计?】 控制反转与安全防线。系统严格杜绝任何 App 在自个儿地盘私自偷偷“拉窗帘”起应用组件。全部动作必须向大管家通报并交由操作系统 IPC 层核发权限和管理显示内存池,以防止失控霸占和极巨的安全越权隐患。
阶段二:重重审查并准备腾挪舞台 (system_server 总部大本营)
电话送达剧院大本营(system_server),ATMS 绝不会亲力亲为,而是全盘托管给干员实干派 ActivityStarter 进行身份查证和调度指令下发。
【执行纪要】:
5. ATMS.startActivity(): 总部接线处理处。
6. ActivityStarter.execute(): 第一站必须去查档案系统户口科 PMS 以查验演员是否有合法演出证。顺利后立刻官方盖戳颁发官方虚拟分身卡片(ActivityRecord)。
7. ActivityStarter.startActivityUnchecked(): 处理极为烧脑复杂的入场位置模式计算(LaunchMode、Flags 推演)。决定把他究竟安置在什么确切地舞台位置(即任务栈 Task 集合)。
8. RootWindowContainer.resumeFocusedTasksTopActivities(): 下达命令开始揭开最高峰的舞台幕布。
9. Task.resumeTopActivityUncheckedLocked(): 卧槽!眼尖的总督惊讶发现目前那个舞台 C 位的亮处居然还站着上一场演出的老演员 A!
【源码洞破与架构本质】:
// fwks/.../wm/ActivityStarter.java
int execute() {
// 【核查局出动】找 PackageManagerService 去对 Manifest 清单扫盘查岗!
ActivityInfo aInfo = mService.resolveActivityInfoForIntent(...);
// 准入一旦通过,系统不会去提前理会你在 App 端那边的类,而是自己在高贵内存池为你建卡 —— ActivityRecord。
ActivityRecord r = new ActivityRecord(mService, ...);
// 推演决断区:他到底要单独新开舞台还是要跑进之前的老片场栈里等算法...
return startActivityUnchecked(r, ...);
}
// fwks/.../wm/Task.java
boolean resumeTopActivityInnerLocked(...) {
// 敏锐爆警:“不准!必须清场!由于单屏显示规矩现在的最前沿舞台还被老页面(比如 A)死赖着不走着!”
if (mResumedActivity != null) {
// 斩首老臣:下发死亡诏书发信号让老页面 A 立即强制下野!(稍后会引发跨 IPC 通知)
startPausingLocked(userLeaving, false /* uiSleeping */, top);
// ⚠️⚠️ 极其震怒且要命的死扣卡口点:这段代码执行后马上霸道阻隔并跳出结束,`return true;` !!
// 这表示系统内部极其庞大运作链路在此被彻底挂起锁死!系统要求:等!你个 B 什么时候等 A 在自己的机器跑完了撤退发回消息,老子什么时候才重新解锁让你 B 上去渲染。
return true;
}
}
【为何这么设计?】 这是一个严谨的高压防并发屏幕混合撕裂序列锁。如果不通过一种等待老页面退下的同步回写锁,在底层双开或者内存突然枯竭时将发生重度的图形堆叠乱象,因为在设备单显物理世界里王权更替只能有一位在山巅。
阶段三:旧主被迫退役以及解冻死锁 (App 端前台的生死相逼)
系统通过 Binder 向 App 原剧团拨回下发勒令:PauseActivityItem 清场包裹函。
【执行纪要】:
10. Task.startPausingLocked() -> 发起 IPC 跨进程反扑!
11. ApplicationThread.scheduleTransaction() : 剧团前台电话客服中心接到勒令卷宗。
12. ActivityThread.H: 走内部 Handle 线程切流发送到大厅核心主线消费队列。
13. TransactionExecutor.execute(): 主簿开始核对系统剧本册。
14. ActivityThread.handlePauseActivity()
15. Activity.performPause() -> onPause(): 老演员 A 触发脱下华盖执行退场动作!
16. ActivityTaskManagerService.activityPaused() : 剧团前台马上再次飞鸽传书回拨通告极度焦急的 ATMS 皇帝陛下:“报!A 已经成功摘冠离开舞台中心区域了!!”
【源码洞破与架构本质】:
// fwks/.../app/ActivityThread.java
@Override
public void handlePauseActivity(...) {
// 这行强横无理地捅进内核,最终这处代理触发了 A 面里你们这些倒霉开发者手写的保护墙 Activity.onPause()!!
performPauseActivity(r, finished, reason, pendingActions, ...);
// (注意了兄弟!)上面那步你的破 onPause() 不管怎么作妖终于跑完后,这行代码是这辈子最重要的解封神器——
// App 端主动调用系统 Binder 重大长途接口,交回王权兵符:“老臣彻底滚出画框了!陛下可以安排那个新来的小子上台了!”
ActivityTaskManager.getService().activityPaused(r.token);
}
【为何这么设计?与史诗级天坑防雷】
为什么说 onPause 是整个跳转过程中的大忌、里面做磁盘同步会直接废掉下个界面?
因为如果你在这(老老实实老演员 A 脱衣服离场的函数里)强行做个 I/O 网卡 500 毫秒卡顿,导致下面那个向 ATMS 去告平安的“ activityPaused 长途电话”整整被延误打了出去。
而在上一步中看到没有,ATMS 全体在系统层被霸道死死 return true 的黑锁中僵直着根本不敢放下一个动作下去。卡顿就是如此这般发生在了新旧老王的权利更替延迟之中!
阶段四:皇帝赐轿,新王驾到!包裹空投 (ATMS 解链放行)
ATMS 终于收到了上面等了半天的破冰通通报环,总开关锁被啪的开启!开始向系统要求造这个一直嗷嗷待哺的新老演员。打包包裹送往民间!
【执行纪要】:
17. ATMS 解除挂锁后复活并狂奔流转终于抵达: ActivityStackSupervisor.realStartActivityLocked()。
18. ClientLifecycleManager.scheduleTransaction(): 装箱一个名为 ClientTransaction (客户端事务宣告机)的“原子复合大包裹”,跨进程向剧团(App进程端)狂飙核弹。
【源码洞破与架构本质】:
// fwks/.../wm/ActivityStackSupervisor.java
void realStartActivityLocked(ActivityRecord r, WindowProcessController proc, ...) {
// 【封装圣旨】:基于新系统革命机制,打包出一个极度规范的且自驱的装配剧本事务包(ClientTransaction)
final ClientTransaction clientTransaction = ClientTransaction.obtain(
proc.getThread(), r.appToken);
// 动作装配匣 1:装填火药 "实体造物指令" (LaunchActivityItem) -> 内含逼迫 App 中进行对象无中生有被造的暗桩,且最终要求调用完其内部 onCreate!
clientTransaction.addCallback(LaunchActivityItem.obtain(...));
// 动作装配匣 2:设立瞄定准星 "终极长远目标" (ResumeActivityItem) -> 我不要管你中间如何摔跤,这包货里面定死了:你的最终生命线停止标尺必须给我抵达 onResume 的前台展现台!
clientTransaction.setLifecycleStateRequest(ResumeActivityItem.obtain(...));
// ⚡️ 将带上无数期待和命运齿轮的庞大 IPC 通信狂掷而出!剧本成真!
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
}
【为何这么设计?】 在十分古早恶劣环境的 Android 8 时代及前夕,这一步并不是打包这么漂亮的,它是在底层狂掉线发好几遍零散的 scheduleLaunch 包裹甚至后来又发 scheduleResume 包。试想一下极易翻车的非稳态 CPU 分段抢权内导致接收的极度错顺和生命周期交叉崩坏。用巨大的 ClientTransaction 的降维宣告式大原子包裹操作方式就是提升生命鲁棒大一统的极度基石体现!
4. 后台作坊的孵化奇迹:从拆包裹再到 onCreate() 的显灵开拔
终于历经九死一生大剧本 LaunchActivityItem 回流了我们亲切的 App 本家环境的 ActivityThread 里。它要正经开始去创造一个无中生有的活人(进行深空探索引擎 performLaunchActivity() 方法)。一切伟大发源的实质被这微型的三个巨幕笼罩:
① 反射造体(凭空捏人形)
再逆天的超神器四大组件,它如果想变成实物首先得具备一个肉体:
// fwks/.../app/ActivityThread.java
try {
// 大惊失色的操作:强逼当下的内存 ClassLoader 在虚拟机堆区通过纯空参的反射,干硬地生成该实体!
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
}
【比喻真意】:通过泥巴瞎勾勒出个人的空架子躯壳。此时它根本还不足挂齿调用大方:去写个 getString 它都要报错!因为它身上这会连任何经脉环境都没有,游离三界毫无权柄法力可谈!
② 法令打光,资源注入并授神格(Context / Window 合成)
想让它不仅成为能发号施令,最重要是能画前台的仙人。
try {
// 给他创建极其硬派且只专属于他的独木通神经大树系统:ContextImpl !!
ContextImpl appContext = createBaseContextForActivity(r);
// 【逆境翻盘!!】:伟大史诗方法调用:强行从天灵盖植入接源! attach 通脉!!
activity.attach(appContext, this, getInstrumentation(), r.token, ...);
}
【比喻真意】:穿透进 Activity.attach() 底层极其庞大,但干的无外乎就那三桩通天气机:
- 经底基绑大换血:把上面弄到的带有巨大访问能力的
ContextImpl生硬塞进了自身根茎基类,获得了在系统中发光要资源要调度的极尽魔方ContextWrapper。 - 画轴封赏:向这个类上塞入了一个唯一用来承载并可代理天下一切绘画触控屏幕的神图画布实例:
mWindow = new PhoneWindow(...)。 - 大内令牌挂坠:把第四大步骤远方传过来的证明身份合法的那个系统颁发唯一官方信证:
IBinder Token贴着胸口保存挂靠,方便自己今后被动清杀下撤时拿来举证这可是圣旨不杀不罚。
③ 鸣枪出关开舞台(onCreate 的猛烈破土!)
好饭备齐。系统底层收束封印,对向你暴露最后且真正合规的可控第一口空气大门代码段:
// 躯干装备全部备好具备神机造化力,代理管家让演员大步破幕推走大场开门!
mInstrumentation.callActivityOnCreate(activity, r.state);
此句神来之手层层解套入骨后最后引发大爆炸的地方就是你手写覆盖的:protected void onCreate(...)。
当这一声震天鼓在主线程上猛地敲出以后,你那在第一段代码位置调的 setContentView(...) 此时由于背后早就藏好立下的那方大画板实体 PhoneWindow 加载完毕!进而这开始使得千奇百怪的视图和布局在此生根解析生根!!
5. 极端的算法:如何修补那落差万丈的残本引擎状态机
我们仔细且后怕地发现在上面的流转中仅仅完成了所谓的 Launch 后代码居然只到 onCreate 就悄然而断不再下探?
你可千万别忘了前面剧团可是明确签下生死目标一定要跑到终点站前台宣演的 ResumeActivityItem 啊!!剩余这可观的时差距怎么被补齐的?!
我们只能仰天惊奇于 Android Framework 源码下引擎内部那极其先进如同异形蜘蛛一般的推算引擎全自动造路器逻辑:
// fwks/.../app/servertransaction/TransactionExecutor.java
private void executeLifecycleState(ClientTransaction transaction) {
// 拿到那个包裹最终刻上的无上红线硬指标
final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
// 【探量自身现处于何种锚位】:因上面造物的打板刚才已经耗尽跑完 onCreate 遗漏了身位被留在可怜的深位如等级 1 区。
int currentState = r.getLifecycleState();
// 【查看目的地高悬的彼方段位】:这里写的很死大目标必须逼进 Resumed 常数高维度之境 如等级 3 区。
int targetState = lifecycleItem.getTargetState();
// !无敌的算道引擎补齐器启动:从阶段 1 强制爬升强迫符合阶段 3!必须依靠推演拿取那极其惊悚包含路牌缺失断层的桥梁集!
// 测算得出结果必走路标大号数组集是:[ON_START, ON_RESUME]!
final IntArray lifecycleItemRequest = mHelper.getLifecyclePath(
currentState, targetState, excludeLastState);
// 手握测算残缺单子,系统强制开启一个霸道顺丰车大 FOR 循环!利用底层反射疯狂从短线接管顺路去踩点爆开它!
performLifecycleSequence(r, lifecycleItemRequest);
}
在那巨大的黑匣子 performLifecycleSequence() 之内:引擎抽血猛追!撞到了算出来的第一个缺位单 ON_START,马上被截取代理到这人对应的 handleStartActivity();紧随狂奔撞到算出来的最后一个硬性单列 ON_RESUME,猛灌切交给了 handleResumeActivity() 以获得至高大满贯合规闭环线索。
【降维的架构美学】:为什么要有这套堪称鬼斧神差的自驱填海算子体系?! 这是防止跨极远双进程来回丢包裹后产生极度脱节导致部分阶段跑了但前台依然拉黑幕的残败景象。这种声明出高傲目标却通过底边强制检测断层随后强迫以强硬填漏机制逼退差值数组的手法!,彻底一统消灭了古老因单打发送 Binder 发送拥塞或极差硬件底层乱飞丢失乱步的灾难时空错乱现象!它是现代移动多态调位引擎极其严密的齿轮核心心跳之一!
6. LMK 的极速断头台与 onDestroy 的终极骗局
作为文章真正的基座级防雷收尾,我们要根除常年的最大认知误区。
你总想着把关键保存的变量放置在生命周期最末端的地方清理清零,却不知 在一个具备高压系统管控的操作系统内,从来不存在有能百分百担保能唤起 onDestroy() 的承诺!一切只是建立在你顺位祈愿的高级谎言上。
只有在你程序调用 finish() 时,或配置环境(变黑白切换或猛然被强制折叠屏扯开导致屏幕丈量失效)的合宪合规下令时,系统它才会像一位礼仪大师那般温和不抗:按程序规则层级先吐纳清排让生命 Stop 等它跑够退出来清理后台以执行最后挽歌 Destroy 为演员妥善料理最后一切痕迹。
然而大多你遇见的实战世界远比你这悲凉得多:当你划掉后台或正在吃大分的高能耗乱战内存局内导致进程等级极坠被后台 Linux 核底层直接视若废品划为下贱之境(核心命脉 oom_adj 被猛降至底层底边)。如果凑巧被前面哪个需要高内存大发病的高清画质吃分游戏或高负载主任务急迫逼讨内存槽时系统极度短缺危机将触角大反转激昂出最为大逃杀级别的极巨清剿体系: LMK (Low Memory Killer) 大斩龙剑落下大网扫向全部底层虚空内存底池!!
它一旦被这般残酷的杀力判定激活。
下发的便是最高权限冰凉彻底且不会讲半句多余核准判语的纯物理最上游切割封号终令: kill -9 (SIGKILL)。
这个完全剥夺任何生命防卫手段终极核断令将会毫不迟疑地彻底横穿并且从上方全维直接下穿彻底豁免并且轰破你的整个庞大神话般的 JVM\ART 执行全域护航封印结界。
一飞秒!就在区区的一飞秒间将整个应用包裹进程内存连带着你的一切可骄傲执行的 Java 引擎核心化作这大宇宙大计算机背景深黑中的彻底尘土与齑粉。
在系统无声狂怒宣判下,它不可能连去拨出零点一纳秒的闲心去准许你在系统内存挂起的任何形式的如 ShutDownCallBack 等微末挂件线程遗篇中挣扎,更不用多去考虑去执行系统派回给所谓引擎调度员去大跨步再转接给你可悲死气全无的底线回调函数 onDestroy() 以做关门与最后哀悼之词等无谓的怜悯残章碎影之息了。所有想把系统释放压押宝或者做保存行为打算在它才动作的幻想,无一例外这刹那随着进程彻底蒸发成为导致你那些业务彻底挂掉无法还原缺失且永世悬空缺角缺页的遗漏错断天罪 BUG。
只有洞明上述这等一切大局进程撕扯对局且了然遥远调线引擎以及那在任何一刹那高悬在上空绝然挥舞的不可回溯下砍狂斩巨剑物理斩首。当你碰见屏幕翻切遗失,重卡无果锁表等等疑云死灾卡关死角天谴卡死绝境时。你手握源码那不可动摇且毫不含糊其辞防守向退于真正强力抗衰与断弦避雷保全线也就是全面退于真正的阵线 onPause / onStop / ViewModel 闭环。
至此!这方才能在这危机与惊悸的庞大的虚拟底海沙盒大盘之界锻造写出坚逾最冷高厚钢板最底层最彻底稳当安全不死的大乘防灾强悍至高鲁棒避绝代码真理全篇。