StartWindow

介绍

在 Android 中,StartWindow(启动窗口) 是系统在启动一个新的 Activity 时,为了避免界面出现明显的“白屏”或者“黑屏”闪烁,而临时添加的一个窗口。它的主要作用是在真正的 Activity 界面初始化、布局、绘制完成之前,给用户展示一个过渡界面,提升启动体验。

核心概念

名称 作用
StartWindow 应用启动时,系统自动为新的 Activity 创建的临时窗口,用于掩盖应用真正 UI 初始化过程。
StartingWindow 与 StartWindow 同义,源码中通常使用 StartingWindow 来描述。
SplashScreen(Android 12+) 是 StartWindow 的现代化替代方案,系统提供标准的样式和动画过渡能力。

创建时机与源码位置

当你启动一个新的 Activity 时(包括应用首次启动或从其他 Activity 跳转),系统会尝试创建一个 StartWindow:

// frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
boolean createStartingWindow(...) {
    ...
    mWmService.mStartingSurfaceController.addStartingWindow(this);
}

具体流程如下:

1. 启动 Activity(AMS 层 startActivity)

2. ActivityRecord#createStartingWindow() 判断是否需要创建 StartWindow

3. StartingSurfaceController#addStartingWindow() 创建 StartingWindow(通常是 SplashScreen 或空白背景)

🧾 StartWindow 内容由谁决定?

1. 主题样式决定背景颜色或图片:

Manifest 中声明的 android:theme 控制了启动时窗口的视觉效果。

<activity
    android:name=".MainActivity"
    android:theme="@style/Theme.App.Starting">

• 若该主题设置了 windowBackground,系统将使用该背景作为 StartWindow 内容;

• 若设置了 windowSplashScreenAnimatedIcon(Android 12+),则展示动画图标。

2. 是否展示 StartWindow 由系统判断:

系统会根据是否是首次启动、是否冷启动、是否设置窗口背景等条件决定是否展示。

移除时机

StartWindow 在 目标 Activity 的窗口真正准备好展示之后 被移除。即:

• 当目标 Activity 执行完 onCreate() → setContentView() → View 被绘制 → reportDrawFinished();

• 或系统通过 ActivityRecord#handleLaunchActivity() 设置 DecorView 完成绘制;

系统会通过如下代码关闭 StartWindow:

// frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
void removeOrphanedStartingWindow(boolean activityRemoved) {
    mWmService.mStartingSurfaceController.removeStartingWindow(this, activityRemoved);
}

💡 Android 12 后的变化:SplashScreen API

StartWindow 的官方增强版(解决黑白屏问题)

从 Android 12 开始,StartWindow 被 SplashScreen API 替代,支持:

• Animated Icon

• Exit Animation

• 支持 setKeepOnScreenCondition 控制移除时机

实战建议

使用 SplashScreen API(Android 12+):在 onCreate() 中调用 installSplashScreen();

老版本兼容方案:使用 windowBackground 设置启动背景,或者搭配透明主题展示自己的 SplashActivity;

注意 windowDisablePreview:设置 android:windowDisablePreview=”true” 可禁用 StartWindow,但会导致短暂白屏或黑屏。

和最终展示的window的关系

StartWindow 是 Android 启动过程中用于“过渡”显示的临时窗口,通常用来避免应用启动时的“白屏”或“黑屏”。它在 Activity 启动初期由系统自动添加,等应用自身的 UI(也就是最终的 Window)准备好之后再将其移除。

下面我从它的生命周期、作用、实现和最终 Window 的关系等方面详细说明:

一、StartWindow 的作用

1. 避免白屏

在 Activity 还未完成布局绘制之前,先用一个占位窗口给用户一个视觉反馈。

2. 快速响应

系统可以立即展示 StartWindow,因为它不需要等待应用进程完成创建和布局。

3. 承接启动主题

StartWindow 通常使用 <activity android:theme=”…”> 中定义的 windowBackground 作为展示内容。

二、StartWindow 和最终 Window 的关系

特性 StartWindow 最终的 Window(DecorView)
所属进程 系统进程(SystemServer) 应用进程
添加时机 Activity 启动 early stage Activity onCreate 后,执行 setContentView()
目的 临时 UI 过渡 应用的真实 UI
移除时机 应用绘制第一帧并完成 reportDraw() 后 持久展示
视觉内容 启动主题背景、应用图标(可配置) 应用布局

三、实现原理(流程)

1. 添加 StartWindow

在 ActivityRecord#createStartingWindow() 中添加:

mWmService.mStartingSurfaceController.addStartingWindow(this);

这个 StartingWindow 是通过 PhoneWindow 构建一个 DecorView(但不在 App 进程内),主要由如下类负责:

• StartingWindowController

• StartingSurfaceDrawer

• WindowManagerService

2. 应用 Window 加载完成

应用进程通过如下方式创建并显示最终的 Window:

Activity.onCreate() {
    setTheme(R.style.AppTheme);  // 恢复正式主题
    setContentView(R.layout.xxx); // 最终 UI 显示
}

这时会触发 WindowManager.addView() 添加 DecorView。

3. 移除 StartWindow

在首次绘制完成之后,会走到:

WindowManagerGlobal.reportDrawFinished()
 → WMS.handleAppShown()
 → removeStartingWindow()

最终将 StartWindow 从系统窗口中移除。

四、开发中相关注意点

Theme 的继承:StartWindow 用的是 android:theme 中的内容,最好使用 @style/Theme.App.Start,避免复杂布局。

透明背景会导致黑屏:如果 windowBackground 是透明或没有设置,StartWindow 就失效。

不要做耗时操作:onCreate() 里的耗时会延迟最终 Window 绘制,也会拖慢 StartWindow 的移除。

五、图示结构关系(简化)

[System Process]                          [App Process]
┌──────────────────┐                     ┌────────────────────┐
│ StartWindow      │  ← addWindow() ←   │  ActivityThread     │
│ (Window)         │                     │  onCreate()         │
└──────────────────┘                     │  setContentView() →│
                                         │  attach DecorView →│
                                         └────────────────────┘
         ↓ 等待首帧绘制完成
         removeStartWindow()

不是同一个PhoneWindow

StartWindow和展示DecorView的窗口是一个窗口?对应一个PhoneWindow?

不是一个窗口,也不对应同一个 PhoneWindow

一、简短结论

对象 StartWindow DecorView 所在窗口
所属进程 SystemServer(系统进程) App 进程
所属 Window 类 BaseWindow(内部类) PhoneWindow
Window 类型 临时窗口 主窗口(正式)
添加位置 WindowManagerService 添加系统窗口 WindowManagerGlobal 添加应用窗口
View 根节点 系统构造的简易 View(一般含背景、图标) 应用自己构建的 View(setContentView)

二、源码级说明

1. StartWindow 是系统添加的独立窗口

在 ActivityTaskManagerService 的 realStartActivityLocked() → addStartingWindow():

// frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
boolean createStartingWindow(...) {
    mWmService.mStartingSurfaceController.addStartingWindow(this);
}

这个 addStartingWindow() 内部会创建一个 系统进程里的临时 View 层级,通过 WindowManagerService.addWindow() 添加到系统的窗口列表中。

系统会使用 app 的启动主题(例如背景色、logo)来构造一个 View 并显示出来,这个 View 不属于你的应用进程。

2. DecorView 是 App 自己构造并添加的主窗口

在 ActivityThread.handleResumeActivity() 中:

r.window = r.activity.getWindow(); // 是 PhoneWindow
View decor = r.window.getDecorView();
wm.addView(decor, l); // 应用自己添加窗口,WindowManagerGlobal → ViewRootImpl

此时应用在自己进程中创建 DecorView,通过 ViewRootImpl 连接到系统服务,最终出现在屏幕上。

这才是真正属于 App 的窗口,也是 setContentView() 设置内容之后真正展示的窗口。

三、两者不是同一个窗口的证据

行为 说明
StartWindow 添加 WindowManagerService 通过 addWindow() 添加系统级窗口,token 是 Activity 的
DecorView 添加 ViewRootImpl.setView() 最终调用 WMS 的 addWindow(),但是一个新的 Surface 和 View 树
StartWindow 移除 系统在收到应用“首帧绘制完成”信号后移除这个独立的 View
DecorView 出现 应用第一次 doTraversal() 完成绘制 → SurfaceFlinger 显示

四、可视化对比

[System Process]
 ┌─────────────────────────────┐
 │ StartWindow                 │ ← 通过 WMS.addWindow 添加
 │ View: 背景+图标             │
 │ Token: Activity 的 token    │
 └─────────────────────────────┘
        ↓(等待 App 首帧完成)
        移除窗口

[App Process]
 ┌─────────────────────────────┐
 │ PhoneWindow                 │ ← Activity.getWindow()
 │ ┌─────────────────────────┐ │
 │ │ DecorView               │ │ ← setContentView() 设置 View 树
 │ └─────────────────────────┘ │
 └─────────────────────────────┘
        ↓(首次 Vsync 后绘制)
        ViewRootImpl.draw → 展示主 UI

五、补充:它们之间的连接点是 token

系统之所以能把 StartWindow 和应用主窗口关联起来,是因为:

• 两者都绑定了同一个 WindowToken

• App 的 Activity 还未真正 attach DecorView 之前,系统先用这个 token 显示了 StartWindow;

• App 在 resume 时,通过相同的 token 注册正式窗口,系统完成替换。

六、结论

StartWindow 是一个独立的、系统进程中创建的临时窗口;

DecorView 所在窗口才是应用真实的主窗口;

二者不是同一个 PhoneWindow,也不是同一个 ViewRootImpl,但使用同一个 token 实现关联。

实现原理

在 Android 系统中,StartWindow(启动窗口)是为了提升应用启动体验而设计的临时窗口。它在应用的主界面(最终的 Window)尚未准备好时提供视觉反馈,避免用户看到空白或黑屏。以下是 StartWindow 的详细实现过程,包括其添加与移除的关键代码路径:

一、StartWindow 的添加流程

当应用启动时,系统会在应用的主界面加载完成前,添加一个启动窗口。这个过程主要涉及以下关键类和方法:

1. ActivityTaskManagerService: 负责管理应用的任务和活动。

2. StartingWindowController: 协调启动窗口的添加和移除。

3. StartingSurfaceDrawer: 实际负责绘制启动窗口的内容。

在启动过程中,系统会调用 StartingWindowController 的 addStartingWindow() 方法,该方法进一步调用 StartingSurfaceDrawer 的 addSplashScreenStartingWindow() 方法来创建和显示启动窗口。

二、StartWindow 的移除流程

一旦应用的主界面准备就绪,系统会移除启动窗口。这个过程主要涉及以下关键步骤:

1. 应用绘制完成通知: 应用在完成首帧绘制后,会通过 reportDrawFinished() 方法通知系统。

2. 系统移除启动窗口: 系统接收到绘制完成的通知后,会调用 StartingWindowController 的 removeStartingWindow() 方法来移除启动窗口。

在 removeStartingWindow() 方法中,系统会检查当前窗口的绘制状态,如果满足条件(如窗口已绘制完成),则会将启动窗口从窗口管理器中移除。

三、关键代码路径示意图

以下是启动窗口添加与移除的关键代码路径示意图:

[ActivityTaskManagerService]
        |
        v
[StartingWindowController.addStartingWindow()]
        |
        v
[StartingSurfaceDrawer.addSplashScreenStartingWindow()]
        |
        v
[WindowManagerService.addWindow()]
        |
        v
[显示启动窗口]

当应用主界面绘制完成后:

[应用进程]
        |
        v
[reportDrawFinished()]
        |
        v
[系统接收通知]
        |
        v
[StartingWindowController.removeStartingWindow()]
        |
        v
[WindowManagerService.removeWindow()]
        |
        v
[移除启动窗口]

四、开发注意事项

启动主题配置: 确保在 AndroidManifest.xml 中为启动的 Activity 配置合适的主题,包括 windowBackground 属性,以便启动窗口能够正确显示。

避免透明背景: 使用透明背景可能导致启动窗口无法显示,建议使用不透明的背景色或图片。

优化首帧绘制时间: 在应用的 onCreate() 方法中避免执行耗时操作,以加快主界面的加载速度,从而尽快移除启动窗口。

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器