启动白屏

介绍

在 Android 中,**“白屏”**指的是应用启动过程中,在 Activity 的界面还未准备好时,系统为了避免黑屏而默认显示的一块白色背景,这会造成用户看到一个纯白界面,体验不佳。

白屏产生的根本原因

主要出现在 冷启动(Cold Start)过程中,应用还未绘制出第一个 Activity 的界面时,系统需要显示一个占位界面,这时就可能出现“白屏”:

具体产生流程如下:

1. 启动 Activity 时,系统先创建一个 Window。

2. 还未执行 setContentView(),还没有 UI 被绘制;

3. 为了不显示黑屏,系统会为这个窗口 填充默认背景

4. 如果没有配置其他内容,这个默认背景就是 纯白色

5. 当真正的 UI 渲染完成后,白屏才会被覆盖。

为什么默认是白色的?

系统在启动 Activity 时,会根据 theme 给窗口一个背景。比如:

<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
    <!-- 若没有指定 windowBackground,则默认是白色 -->
</style>

如果你没有明确指定 android:windowBackground,那么系统就使用了默认背景,而这个背景通常就是白色。

如何避免白屏?

1. 使用 SplashScreen API(Android 12+)

2. 设置启动背景-避免白屏-不使用SplashScreen 阅读: StartingWindow相关文章

3. 将首帧绘制尽量提前(减少 Application 和 Activity 的初始化逻辑耗时)-避免白屏-不使用SplashScreen

原因分析

从 Android 启动 Activity 的源码角度分析「启动白屏」问题,本质是在 ActivityThread 创建 Activity 到界面完成绘制之间,系统已经将窗口显示出来了,但还未绘制正式内容,这时窗口显示的就是默认的背景(通常是白色),我们称之为“启动白屏”。

当 setContentView() 被调用之后,DecorView 被创建并添加到 ViewRootImpl,此时窗口会添加到 WMS 并显示。但由于首次绘制未完成,此时用户看到的是 DecorView 的默认背景 —— 这就是“白屏”现象。


························必读·······················

白屏是发生在:调用 setContentView() 后,DecorView 被创建并添加进系统窗口,但真实 UI 内容尚未绘制出来的那个阶段。所以所有setContentView()之前的操作,以及setContentView()本身的绘制时间,都会影响白屏的持续时间。包括Application.onCreate()中的操作,所以优化白屏的思路,就是减少setContentView()之前的代码的执行时间,同时配合设置启动背景来进行优化.注意,设置的启动背景是在Activity的onAttach方法中生效的,如果在onAttach方法之前进行的耗时操作,比如Application.onCreate()中的操作,仍然会影响到白屏,所以Application.onCreate()中也不要进行耗时的操作.

························必读························


下面我从源码路径和执行时序的角度详细拆解整个过程,并指出白屏产生的关键时刻。

🧭 1. 启动流程简略图(重点时序)

→ AMS 进程(ActivityManagerService)
    ↓
→ Application 进程(ActivityThread)
    ↓
1. ActivityThread.handleLaunchActivity()
    - 创建 Activity
    - 调用 Activity.attach()
        → 创建 PhoneWindow
    - 调用 Activity.setContentView()
    - 调用 Activity.onResume()
2. ViewRootImpl.setView()
    - 创建 ViewRootImpl,设置 DecorView
    - 添加窗口(WindowManagerService)
    - 发起绘制流程 performTraversals()

📌 2. 白屏产生的根本原因

白屏出现的关键是:

Window 提前添加到了 WMS 中,DecorView 的背景被绘制出来了,但 setContentView() 还没执行,界面没有真正内容。

也就是 Activity.attach() 阶段 Window 就已经被加入,且 DecorView 的默认背景(如白色)开始展示。

🔍 3. 关键源码路径追踪

3.1 ActivityThread.handleLaunchActivity()

public Activity handleLaunchActivity(ActivityClientRecord r, ...) {
    Activity a = performLaunchActivity(r, customIntent); // 创建 Activity
    ...
}

3.2 performLaunchActivity()

Activity activity = mInstrumentation.newActivity(...);
Application app = r.packageInfo.makeApplication(...);
...
activity.attach(...); // ❗关键,创建 PhoneWindow 并绑定 DecorView
...
activity.onCreate(...); // ❗此时才开始 setContentView()

3.3 Activity.attach()

mWindow = new PhoneWindow(this);
mWindow.setWindowManager(...);

这里会设置 WindowManager,同时会加载Android Manifest中设置的背景,阅读:Manifest中设置的android_theme什么时候生效

在Activity的onCreate方法中调用的setContentView中会调用构建 DecorView的方法,阅读:DecorView的创建

public void setContentView(int layoutResID) {
    // 此时才开始设置你定义的布局
    mWindow.setContentView(layoutResID);
}

📦 4. Window 添加流程

当你设置完布局后,系统会通过 WindowManager 添加 View:

ViewRootImpl.setView() {
    mView = decor;
    ...
    requestLayout(); // 发起首次绘制
}

在 requestLayout() 中,会触发 performTraversals(),开始测量、布局、绘制:

ViewRootImpl.performTraversals() {
    // 调用 view.measure(), view.layout(), view.draw(canvas)
}

此过程是异步的,即 DecorView 添加到 WMS 后,不代表界面立即可见内容,绘制过程尚未完成。

正确流程梳理:

步骤 描述
1. Activity.attach() 创建 PhoneWindow,但未创建 DecorView
2. Activity.setContentView() 创建 DecorView 并添加用户布局(inflate)
3. handleResumeActivity() 获取 DecorView 并通过 WindowManager 添加进系统窗口
4. ViewRootImpl.setView() 设置 DecorView,触发首次 measure/layout/draw
5. DecorView 背景被绘制 若内容还未绘制完,只显示背景,造成“白屏”

❗为什么出现白屏?

系统出于启动优化考虑,提前显示了窗口,但此时应用主界面还没设置上来,显示的是 DecorView 的默认背景。

在未设置主题前,它的默认背景就是:

<style name="Theme.DeviceDefault">
    <item name="android:windowBackground">@android:color/white</item>
</style>

✅ 5. 避免白屏的优化方案

✅ 5.1 使用 windowSplashScreenBackground

Android 12+ 引入的 SplashScreen API,建议使用:

<!-- themes.xml -->
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
    <item name="android:windowSplashScreenBackground">@color/launch_background</item>
</style>

✅ 5.2 自定义启动主题 + 替换背景

<!-- res/values/themes.xml -->
<style name="AppTheme.Launch" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowBackground">@drawable/splash_bg</item>
</style>

并在代码中设置:

override fun onCreate(savedInstanceState: Bundle?) {
    // 设置为正式主题(恢复)
    setTheme(R.style.AppTheme)
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
}

⚠️ 注意:切换主题要在 super.onCreate() 之前。

📌 总结

阶段 描述
Activity.attach() 创建 PhoneWindow,DecorView 初始化,添加到 WMS
Activity.onCreate() setContentView 设置正式布局
ViewRootImpl.setView() 设置 View,触发首次绘制
首次绘制完成前 DecorView 的背景被绘制(即白屏)

白屏的核心原因在于:DecorView 提前展示,但未设置实际内容布局。

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