ARTICLE · 1122300
深入剖析为什么app热启动Activity可以实现秒开体验?
背景
昨天国庆放假期间,有vip学员也在学习马哥性能相关课程,有个同学也提出疑问:“热启动虽然很快哪怕耗时100ms,虽然比冷启动短了很多,但那也是有一定时间延时的?但是真实打开热启动的app时候,感觉非常流畅,丝毫没有感觉到有任何的卡顿。”
针对这个问题其实解答起来就需要对冷启动app和热启动app整个过程进行抓取相关的perfetto分析。
perfetto抓取分析对比法
前面课程文章有对冷启动的Splash Window进行了详细的,但是大家是否有想过热启动时候的是否也需要有类似的Splash Window呢? 答案是肯定的,具体可以通过WinScope或者Perfetto抓取暖启动的过程来验证:

其实桌面的暖启动也会有一个StartingWindow,名字叫做SnapshotStartingWindow,他的启动时间明显比冷启动SplashWindow要快很多,几乎为SplashWindow时间的1/2,可以看到点击后,SnapshotStartingWindow在68ms后就进行了上帧。
再看看冷启动的SplashWindow的时间:
可以看出桌面点击后,要118ms才有Splash window的绘制帧数据。
SnapshotStartingWindow数据是啥?
冷启动时候大家都知道SplashWindow数据就是ICON + 背景色的样子
但是暖启动时候SnapshotStartingWindow显示内容是啥呢?
实际上他显示内容就是类似最近任务一样的,是activity内容的截图 获取方式: 截图文件保存在:
/data/system_ce/<userId>/snapshots/<随机UUID>/<taskId>.jpg
/data/system_ce/<userId>/snapshots/<随机UUID>/<taskId>.proto
/data/system_ce/<userId>/snapshots/<随机UUID>/<taskId>_reduced.jpg
实战如下:
adb pull /data/system_ce/0/snapshots/09c8fa2d-ff1b-482f-882c-eb1e0b5ca51d/789.jpg
/data/system_ce/0/snapshots/09c8fa2d-ff1b-482f-882c-eb1e0b5ca51d/789.jpg: 1 file pulled, 0 skipped. 19.7 MB/s (86913 bytes in 0.004s)

为什么感知不到SnapshotStartingWindow存在呢?
其实感知不到SnapshotStartingWindow存在就是系统设计这个SnapshotStartingWindow成功的表现,本身就是想让你无感的点击图标后进入到Activity真实内容,但是因为Activity真实内容显示是需要一定时间的,哪怕暖启动。
那么在真实Activity绘制前这段时间就需要使用SnapshotStartingWindow来显示,他显示的内容是上一次Activity退出的task截图。
感知不到SnapshotStartingWindow存在其实有2个因素:
1、SnapshotStartingWindow的显示时间非常短暂,可能是100ms左右,相比SplashWindow显示500ms时间短了很多。
2、SnapshotStartingWindow本身就是显示task的截图,所以内容上和Activity的真实显示内容是高度一致的,所以很难感知出他们的变化,除非画面会有大的变化才可能会有跳变感。
整体流程架构总结
system_server (ATMS/WMS) Shell (com.android.wm.shell)
══════════════════════════ ═══════════════════════════════
ActivityRecord.addStartingWindow()
│
├─ getStartingWindowType()
│ │
│ ├─ newTask/!processRunning → SPLASH
│ └─ taskSwitch + snapshot → SNAPSHOT ← 温启动命中
│
├─ createSnapshot()
│ └─ new SnapshotStartingData(snapshot)
│
├─ StartingSurfaceController
│ .createTaskSnapshotSurface()
│
└─ TaskOrganizerController
.addStartingWindow() ──Binder IPC──▶ TaskSnapshotWindow.create()
│
├─ IWindowSession.addToDisplay()
├─ IWindowSession.relayout()
├─ SnapshotDrawerUtils
│ .drawSnapshotOnSurface()
│ └─ setBuffer(HardwareBuffer)
└─ reportDrawn()
═══════ Snapshot 显示中,App 首帧正在绘制 ═══════
ActivityRecord.onFirstWindowDrawn()
│
└─ removeStartingWindow()
│
└─ TaskOrganizerController
.removeStartingWindow() ──Binder IPC──▶ TaskSnapshotWindow
.removeImmediately()
└─ IWindowSession.remove()

添加路径: ActivityRecord → StartingSurfaceController → TaskOrganizerController → Binder → TaskSnapshotWindow
移除路径: ActivityRecord.onFirstWindowDrawn() → TaskOrganizerController → Binder → TaskSnapshotWindow.removeImmediately()
更多vip干货独享知识讨论,面试题讨论,及面试定制指导上岸fw工程师服务,课程优惠购买成为vip学员进入vip群,积极讨论各种行业难点痛点疑难问题,答疑服务等。
请联系马哥微信:
