重重学app冷启动流程桌面点击到SplashWindow显示与移除流程剖析-aosp16
背景在统计了学员们系统fw面试题后发现桌面点击到app展示第一帧的源码流程这类题目是被考的最多的基本上属于amswms必问的面试题。但是问这类题目绝不是死记硬背可以搞定的哈面试官们都会对流程中的一些点进行灵活考察等当然你只要掌握整个流程无论别人怎么问其实都可以应答如流。本文我们来基于aosp16 r4的源码给大家复习一下原来amswms课程中给大家讲解的SplashWindow的显示与删除流程。1. 冷启动SplashStartingWindow流程概览冷启动Cold Start是指目标应用进程尚不存在需要 Zygote 重新 fork 的场景。SplashStartingWindow核心设计精髓SplashStartingWindow 的创建完全与目标应用进程的生命周期解耦。它在 Zygote fork 之前就已经由 ShellSystemUI进程创建并显示到屏幕上——所以用户点击图标后能秒开看到视觉反馈从而提升app冷启动的用户体验。下面是详细的整体的流程时序图情况主要涉及了5个进程部分2. 第一阶段Launcher 点击 → AMS2.1 点击事件处理文件packages/apps/Launcher3/src/com/android/launcher3/touch/ItemClickHandler.java用户点击桌面图标时ItemClickHandler.onClick()被触发。从 View 的 tag 中取出ItemInfo根据类型分发publicstaticvoidonClick(Viewv){if(itemInfoinstanceofWorkspaceItemInfo||itemInfoinstanceofAppInfo){onClickAppShortcut(v,(WorkspaceItemInfo)itemInfo,source);}}最终通过startAppShortcutOrInfoActivity()构造 Intent调用Launcher.startActivitySafely()文件packages/apps/Launcher3/src/com/android/launcher3/Launcher.java// ~line 2106publicbooleanstartActivitySafely(Viewv,Intentintent,ItemInfoitem){intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);super.startActivity(view,intent,optsBundle);}第一次 IPCLauncher 进程 → AMSIActivityManager.startActivity()。3. 第二阶段AMS 处理 —— 在进程 fork 前创建 Splash3.1 ActivityStarter → startSpecificActivity文件services/core/java/com/android/server/wm/ActivityStarter.java// execute() → executeRequest() → startActivityInner()mRootWindowContainer.resumeFocusedTasksTopActivities();文件services/core/java/com/android/server/wm/ActivityTaskSupervisor.java// ~line 1181voidstartSpecificActivity(ActivityRecordr,...){finalWindowProcessControllerwpcmService.getProcessController(r.processName,r.info.applicationInfo.uid);if(wpc!nullwpc.hasThread()){// 冷启动时 wpc null跳过此分支realStartActivityLocked(r,wpc,...);}else{// ★ 注意调用顺序r.showStartingWindow(true/* taskSwitch */);// 先创建 Starting WindowmService.startProcessAsync(r.processName,...);// 再 fork 进程}}关键showStartingWindow()在startProcessAsync()之前调用。在 Zygote fork 新进程之前Splash Window 已经开始创建。3.2 getStartingWindowType() 决策文件services/core/java/com/android/server/wm/ActivityRecord.java~line 2411privateintgetStartingWindowType(booleannewTask,booleantaskSwitch,booleanprocessRunning,booleanallowTaskSnapshot,booleanactivityCreated,booleanactivityAllDrawn,TaskSnapshotsnapshot){// 冷启动时newTasktrue 或 processRunningfalse → 直接进入此分支if((newTask||!processRunning||(taskSwitch!activityCreated))!isActivityHome){returnSTARTING_WINDOW_TYPE_SPLASH_SCREEN;}if(taskSwitch){if(allowTaskSnapshotisSnapshotCompatible(snapshot)){returnSTARTING_WINDOW_TYPE_SNAPSHOT;}if(!isActivityHome){returnSTARTING_WINDOW_TYPE_SPLASH_SCREEN;}}returnSTARTING_WINDOW_TYPE_NONE;}冷启动中processRunningfalse直接返回STARTING_WINDOW_TYPE_SPLASH_SCREEN。3.3 addStartingWindow → SplashScreenStartingData文件services/core/java/com/android/server/wm/ActivityRecord.java~line 2285booleanaddStartingWindow(Stringpkg,intresolvedTheme,...){// 守门员如果已有绘制完成的窗口不需要 starting windowfinalWindowStatemainWinfindMainWindow(false);if(mainWin!nullmainWin.isDrawn()){returnfalse;}// 获取 Task 截图冷启动时通常为 nullfinalTaskSnapshotsnapshotmWmService.mTaskSnapshotController.getSnapshot(...);// 决定类型finalinttypegetStartingWindowType(newTask,taskSwitch,processRunning,allowTaskSnapshot,activityCreated,activityAllDrawn,snapshot);// ★ 冷启动 → SPLASH_SCREENif(typeSTARTING_WINDOW_TYPE_SPLASH_SCREEN){mStartingDatanewSplashScreenStartingData(mWmService,resolvedTheme,typeParameter);scheduleAddStartingWindow();// ★ 触发创建}}3.4 IPC 到 Shell 进程文件services/core/java/com/android/server/wm/StartingSurfaceController.javaStartingSurfacecreateSplashScreenStartingSurface(ActivityRecordactivity,inttheme){returnmTaskOrganizerController.addStartingWindow(activity.getTask(),activity,theme);}文件services/core/java/com/android/server/wm/TaskOrganizerController.java~line 652StartingSurfaceaddStartingWindow(Tasktask,ActivityRecordactivity,inttheme,...){finalStartingWindowInfoinfonewStartingWindowInfo();info.taskInfotask.getTaskInfo();info.targetActivityInfoactivity.info;info.splashScreenThemeResIdtheme;// ★ IPC 到 ShellmTaskOrganizer.addStartingWindow(taskInfo,info);}4. 第三阶段Shell 进程双线程并行渲染 Splash这是 Android 系统精心设计的性能优化点。4.1 核心思路文件libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java将图标渲染CPU/GPU 密集操作和窗口添加Binder IPC 阻塞操作放到两个线程并行执行。// ~line 123voidaddSplashScreenStartingWindow(StartingWindowInfowindowInfo,intsuggestType){// 线程同步的供应商对象内部使用 wait/notifyfinalSplashScreenViewSupplierviewSuppliernewSplashScreenViewSupplier();finalFrameLayoutrootLayoutnewFrameLayout(context);// 主线程回调在 Choreographer 回调中等待 Worker 的结果并设置内容finalRunnablesetViewSynchronized()-{SplashScreenViewcontentViewviewSupplier.get();// 阻塞等待 WorkerrootLayout.addView(contentView);};// 启动两条并行路径 // 路径AWorker 线程预渲染图标 BitmapmSplashscreenContentDrawer.createContentView(context,suggestType,windowInfo,viewSupplier::setView,viewSupplier::setUiThreadInitTask);// 路径B主线程添加窗口到 WindowManagerif(addWindow(taskId,windowInfo.appToken,rootLayout,display,params,suggestType)){mChoreographer.postCallback(CALLBACK_INSETS_ANIMATION,setViewSynchronized,null);}}4.2 线程同步SplashScreenViewSupplier内部使用synchronizedwait()/notify()实现阻塞等待// ~line 387privatestaticclassSplashScreenViewSupplierimplementsSupplierSplashScreenView{privateSplashScreenViewmView;privatebooleanmIsViewSet;// Worker 线程调用voidsetView(SplashScreenViewview){synchronized(this){mViewview;mIsViewSettrue;notify();// 唤醒主线程}}// 主线程调用 —— 阻塞直到 Worker 完成publicSplashScreenViewget(){synchronized(this){while(!mIsViewSet){wait();// 等待 Worker}returnmView;}}}4.3 添加窗口protectedbooleanaddWindow(inttaskId,IBinderappToken,Viewview,Displaydisplay,WindowManager.LayoutParamsparams,intsuggestType){// ★ 与普通 Activity 窗口完全相同的添加路径mWindowManagerGlobal.addView(view,params,display,null,context.getUserId());}窗口类型为TYPE_APPLICATION_STARTING此时Splash 首帧已显示在屏幕上而应用进程可能还没开始 fork。5. 第四阶段Zygote fork 与应用进程初始化5.1 Fork 新进程startProcessAsync()触发 Zygote socket 通信AMS.startProcessAsync() → ProcessList.startProcess() → ZygoteProcess.start()第三次 IPCSystemServer → Zygotesocket 通信。新进程入口为ActivityThread.main()文件core/java/android/app/ActivityThread.java~line 9238publicstaticvoidmain(String[]args){Looper.prepareMainLooper();ActivityThreadthreadnewActivityThread();thread.attach(false);// 通知 AMS 进程就绪Looper.loop();}5.2 attachApplication → bindApplication → realStartActivityprivatevoidattach(booleansystem){finalIActivityManagermgrActivityManager.getService();mgr.attachApplication(mAppThread);// ★ IPC 到 AMS}第四次 IPC新进程 → SystemServer。文件services/core/java/com/android/server/am/ActivityManagerService.java~line 4583privatebooleanattachApplicationLocked(IApplicationThreadthread,...){thread.bindApplication(...);// IPC 发送应用初始化数据mAtmInternal.attachApplication(app);}RootWindowContainer.attachApplication()遍历属于该进程的顶层 Activity调用realStartActivityLocked()文件services/core/java/com/android/server/wm/ActivityTaskSupervisor.java~line 822booleanrealStartActivityLocked(ActivityRecordr,...){clientTransaction.addCallback(LaunchActivityItem.obtain(...));mService.getLifecycleManager().scheduleTransaction(clientTransaction);}6. 第五阶段Activity 启动与窗口首帧绘制6.1 客户端处理 LaunchActivity文件core/java/android/app/ActivityThread.java~line 4335privateActivityperformLaunchActivity(ActivityClientRecordr,IntentcustomIntent){activitymInstrumentation.newActivity(cl,component.getClassName(),r.intent);activity.attach(appContext,this,...);// 创建 PhoneWindowmInstrumentation.callActivityOnCreate(activity,r.state);returnactivity;}然后是handleResumeActivity()~line 5905publicvoidhandleResumeActivity(ActivityClientRecordr,...){performResumeActivity(r,...);// onResume()if(r.windownull!a.mFinishedwillBeVisible){Viewdecorr.window.getDecorView();decor.setVisibility(View.INVISIBLE);wm.addView(decor,l);// ★ 创建 ViewRootImpl触发渲染}}wm.addView()内部创建ViewRootImpl驱动performTraversals()进行 measure/layout/draw。6.2 首帧绘制完成 → reportDrawFinished文件core/java/android/view/ViewRootImpl.java~line 5454privatevoidreportDrawFinished(NullableTransactiont,intseqId){mWindowSession.finishDrawing(mWindow,t,seqId);// ★ IPC 到 WMS}第六次 IPC应用进程 → WMS。7. 第六阶段onFirstWindowDrawn → Splash 移除7.1 WMS 侧WindowState.finishDrawing()文件services/core/java/com/android/server/wm/WindowState.java~line 5818booleanfinishDrawing(SurfaceControl.TransactionpostDrawTransaction,intsyncSeqId){// Starting window 绘制完成 → 记录指标if(mAttrs.typeTYPE_APPLICATION_STARTING){mWmService.mAtmService.mTaskSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(mActivityRecord);}// ...}绘制状态机转换DRAW_PENDING → COMMIT_DRAW_PENDING → READY_TO_SHOW7.2 performShowLocked() —— 两种窗口的分岔文件services/core/java/com/android/server/wm/WindowState.java~line 4434booleanperformShowLocked(){finalintdrawStatemWinAnimator.mDrawState;if((drawStateHAS_DRAWN||drawStateREADY_TO_SHOW)mActivityRecord!null){// ★ 关键根据窗口类型分发if(mAttrs.type!TYPE_APPLICATION_STARTING){mActivityRecord.onFirstWindowDrawn(this);// 应用窗口 → 移除 Splash}else{mActivityRecord.onStartingWindowDrawn();// Splash 窗口 → 记录已展示}}mWinAnimator.mDrawStateHAS_DRAWN;}设计要点同一个方法处理两种窗口——应用窗口绘制完成时触发onFirstWindowDrawn()移除 SplashSplash 窗口自身绘制完成时触发onStartingWindowDrawn()记录指标。7.3 onFirstWindowDrawn → removeStartingWindow文件services/core/java/com/android/server/wm/ActivityRecord.java// ~line 6508voidonFirstWindowDrawn(WindowStatewin){firstWindowDrawntrue;if(mStartingWindow!null){win.cancelAnimation();}removeStartingWindow();// ★ 移除 Splash}// ~line 2729voidremoveStartingWindow(){if(transferSplashScreenIfNeeded()){// 如果 App 设置了自定义退出动画return;}removeStartingWindowAnimation(true);// 否则走默认动画移除}// ~line 2766voidremoveStartingWindowAnimation(booleanprepareAnimation){if(mStartingData!null){mStartingData.removeStartingWindow(...);// IPC 到 Shell 移除窗口}}通过 IPC 通知 Shell 移除 Splash Window。Shell 侧播放退出动画默认 400~500ms 的渐隐位移动画随后移除窗口。Splash Window 消失应用首帧接管屏幕。总结冷启动 SplashStartingWindow 的完整生命周期Splash 创建startSpecificActivity() → showStartingWindow() → getStartingWindowType() 返回 SPLASH_SCREEN → SplashScreenStartingData → scheduleAddStartingWindow() → IPC → Shell 双线程并行渲染 → Splash 首帧显示 ✓ Splash 移除App 首帧绘制 → reportDrawFinished() IPC → WindowState.performShowLocked() → onFirstWindowDrawn() → removeStartingWindow() → IPC → Shell 播放退出动画 移除 → App 窗口接管 ✓核心设计精髓Splash Window 的创建完全与应用进程的生命周期解耦。冷启动中processRunningfalse系统直接创建SplashScreenStartingData通过 Shell 的双线程并行渲染在 ~1 帧内将 Splash 显示到屏幕。应用进程则需要走完 fork → main → attachApplication → bindApplication → LaunchActivity → onCreate → onResume → addView → 首帧 draw 的完整路径。Splash 就是用来填补这段时间的视觉空白。原文地址https://mp.weixin.qq.com/s/DH0zQZEk2qD46946LdqNNw

相关新闻

范式跃迁,产业落地:2026 上半年国内智能体平台发展全景观察

范式跃迁,产业落地:2026 上半年国内智能体平台发展全景观察

2026 年被全球产业界统一定义为AI 智能体规模化落地元年。上半年,行业彻底告别大模型参数竞赛的单一赛道,竞争重心全面转向智能体平台的工程化、商业化与场景化能力。从顶层政策定调、国内外厂商密集迭代、技术架构革新,到政企大规模采购落地…

2026/7/7 11:08:43阅读更多 →
开源漏洞管理平台Faraday部署与实战指南:从Docker安装到自动化聚合

开源漏洞管理平台Faraday部署与实战指南:从Docker安装到自动化聚合

1. 项目概述:为什么需要 Faraday?如果你是一名安全工程师、渗透测试人员,或者负责企业安全运营,那么“漏洞管理”这个词对你来说一定不陌生。每天,你可能要面对来自各种扫描器、人工测试、第三方报告的海量漏洞数据&am…

2026/7/7 11:08:43阅读更多 →
superpowers-zh:基于Node.js的Claude Code工程化CLI工具链

superpowers-zh:基于Node.js的Claude Code工程化CLI工具链

1. 项目概述:这不是一个“插件”,而是一套让 Claude Code 真正落地的工程化工具链 “斩获20万星!Claude Code 最强插件 superpowers 来了”——这个标题在技术社区刷屏时,我第一反应不是点开链接,而是打开终端敲了三行…

2026/7/7 11:03:43阅读更多 →
DDrawCompat终极指南:3步解决Windows 10/11老游戏兼容性问题

DDrawCompat终极指南:3步解决Windows 10/11老游戏兼容性问题

DDrawCompat终极指南:3步解决Windows 10/11老游戏兼容性问题 【免费下载链接】DDrawCompat DirectDraw and Direct3D 1-7 compatibility, performance and visual enhancements for Windows Vista, 7, 8, 10 and 11 项目地址: https://gitcode.com/gh_mirrors/dd/…

2026/7/7 13:13:55阅读更多 →
TPAFE0808与PIC18F86J50多通道信号采集系统设计

TPAFE0808与PIC18F86J50多通道信号采集系统设计

1. 项目背景与核心器件选型 在工业自动化、医疗设备和实验室仪器等领域,多通道信号采集与控制系统一直是关键的技术需求。这类系统通常需要同时处理多路模拟信号输入(如传感器数据)和输出(如执行器控制),同…

2026/7/7 13:13:55阅读更多 →
MAA明日方舟助手:智能视觉识别驱动的全自动化游戏解决方案

MAA明日方舟助手:智能视觉识别驱动的全自动化游戏解决方案

MAA明日方舟助手:智能视觉识别驱动的全自动化游戏解决方案 【免费下载链接】MaaAssistantArknights 《明日方舟》小助手,全日常一键长草!| A one-click tool for the daily tasks of Arknights, supporting all clients. 项目地址: https:/…

2026/7/7 13:13:55阅读更多 →
WAS Node Suite深度解析:ComfyUI图像处理与批量工作流的技术架构与实战应用

WAS Node Suite深度解析:ComfyUI图像处理与批量工作流的技术架构与实战应用

WAS Node Suite深度解析:ComfyUI图像处理与批量工作流的技术架构与实战应用 【免费下载链接】was-node-suite-comfyui An extensive node suite for ComfyUI with over 210 new nodes 项目地址: https://gitcode.com/gh_mirrors/wa/was-node-suite-comfyui W…

2026/7/7 13:13:55阅读更多 →
国产工业物联网SCADA平台,内置百种协议+物模型引擎,集监控、报警、联动、分析于一体

国产工业物联网SCADA平台,内置百种协议+物模型引擎,集监控、报警、联动、分析于一体

文末联系官方客服! 免费试用、获取源码、申请代理、加入行业交流群!做过工业物联网项目的朋友都懂——设备接入这件事,远比想象中麻烦。前段时间和一个做产线数字化的朋友聊天,他正被一个项目折腾得够呛。客户厂房里几十台设备&am…

2026/7/7 13:13:55阅读更多 →
msAgent:MindStudio一站式调试调优agent,开箱即用

msAgent:MindStudio一站式调试调优agent,开箱即用

​作者​:昇腾实战派 ​知识地图​:https://blog.csdn.net/Lumos_Lovegood/article/details/161601003 摘要 大模型在昇腾AI平台上训练部署时面临Host-Device协同效率低、算子性能下滑、通信延迟大、模型下发慢等问题。传统调优工具(如Mind…

2026/7/7 13:08:55阅读更多 →
从GitHub安全案例解析常见漏洞与防护实践

从GitHub安全案例解析常见漏洞与防护实践

1. 项目概述:从GitHub Trending看安全实战 最近在GitHub Trending上看到一个项目,叫 skills4/skills ,它因为一些安全漏洞案例被大家讨论。这其实是一个挺典型的场景:一个旨在展示或教授某种技能的仓库,本身却成了安…

2026/7/7 4:43:43阅读更多 →
MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

# MLT 2026启示:因果推理与概率建模驱动下一代LLM应用## 一、背景与挑战:从“黑箱预测”到“可信推理”2026年6月,第7届机器学习与趋势国际会议(MLT 2026)将在悉尼召开。会议议程中,“因果与可解释机器学习…

2026/7/7 2:56:31阅读更多 →
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时,通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中,是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

2026/7/7 1:03:28阅读更多 →
Acunetix v24.8 深度解析:DAST漏洞扫描器核心原理与DevSecOps实践

Acunetix v24.8 深度解析:DAST漏洞扫描器核心原理与DevSecOps实践

1. 项目概述:Acunetix v24.8 高级版漏洞扫描器深度解析作为一名在网络安全领域摸爬滚打多年的老兵,我深知一款趁手的“兵器”对于安全测试工作意味着什么。今天要聊的,就是Web应用安全测试领域里一个响当当的名字——Acunetix。特别是其v24.8…

2026/7/7 0:02:41阅读更多 →
国产化信创改造:达梦/人大金仓适配与多数据库兼容方案实战(SpringBoot)

国产化信创改造:达梦/人大金仓适配与多数据库兼容方案实战(SpringBoot)

国产化信创改造:达梦/人大金仓适配与多数据库兼容方案实战(SpringBoot) 🌐 演示地址:http://ruoyioffice.com | 📦 源码1GitHub:ruoyi-office | 📦 源码2GitCode:ruoyi-o…

2026/7/7 0:02:41阅读更多 →
CentOS 7/8 SSH 连接失败:5步系统性排错流程与决策树

CentOS 7/8 SSH 连接失败:5步系统性排错流程与决策树

CentOS SSH连接故障排查:从基础检查到深度修复的完整指南引言当你尝试通过Xshell或其他SSH客户端连接CentOS服务器时,突然遭遇"Connection refused"或"Connection timed out"的错误提示,这种经历对任何运维人员或开发者来…

2026/7/7 0:02:41阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/7 5:11:21阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/7 5:11:21阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/7 5:11:21阅读更多 →