白屏原因分析-源码版本

从 Android 源码角度分析 启动白屏现象,需要深入到 Activity 启动流程、窗口(Window)管理和视图渲染机制。以下是关键源码路径及白屏产生的原因解析:


1. 冷启动流程与窗口初始化

当应用冷启动时,系统会通过 ActivityThreadhandleLaunchActivity() 创建 Activity 实例,并触发窗口初始化:

关键源码路径

  1. ActivityThread.handleLaunchActivity()
    创建 Activity 实例,调用 performLaunchActivity()

    1
    2
    3
    4
    5
    6
    7
    8
    // ActivityThread.java
    public Activity handleLaunchActivity(ActivityClientRecord r, ...) {
    // 1. 创建Activity实例
    final Activity a = performLaunchActivity(r, customIntent);
    // 2. 调用Activity的onCreate()、onStart()、onResume()
    handleResumeActivity(r.token, false, r.isForward, ..., r.pendingResults);
    return a;
    }
  2. Activity.performCreate()
    执行 onCreate(),初始化布局和逻辑:

    1
    2
    3
    4
    5
    // Activity.java
    final void performCreate(Bundle icicle) {
    onCreate(icicle); // 开发者重写的onCreate()
    // 其他初始化...
    }
  3. 窗口初始化与背景绘制
    Activity 启动过程中,系统会为 Activity 创建 PhoneWindow,并设置默认主题背景:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // Activity.java
    final void attach(...) {
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowControllerCallback(this);
    mWindow.setCallback(this);
    mWindow.setWindowManager(...);
    // 应用主题(包含windowBackground)
    mWindow.getDecorView().setBackgroundResource(...);
    }
    • 白屏触发点:在 onCreate() 完成前,系统会先绘制窗口的默认背景(即 windowBackground),若此时主线程因耗时操作阻塞,导致视图(DecorView)未及时渲染,系统会持续显示 windowBackground 定义的背景(默认白色)。

2. 视图渲染与首帧延迟

即使 ActivityonCreate() 完成,视图的真正渲染(首帧显示)仍需通过 ViewRootImplChoreographer 完成:

关键源码路径

  1. ViewRootImpl.performTraversals()
    触发视图的测量(Measure)、布局(Layout)、绘制(Draw):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // ViewRootImpl.java
    private void performTraversals() {
    // 1. 测量
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    // 2. 布局
    performLayout(lp, mWidth, mHeight);
    // 3. 绘制
    if (!cancelDraw && !newSurface) {
    performDraw();
    }
    }
  2. Choreographer 与 VSYNC 信号
    视图渲染依赖 Choreographer 接收 VSYNC 信号后提交帧:

    1
    2
    3
    4
    // Choreographer.java
    public void postFrameCallback(FrameCallback callback) {
    postFrameCallbackDelayed(callback, 0);
    }
    • 白屏触发点:若主线程在 onCreate()onResume() 中有耗时操作,导致 performTraversals() 延迟执行,首帧无法及时提交,系统将持续显示窗口背景(即白屏)。

3. 窗口背景与主题的源码逻辑

系统通过 PhoneWindowDecorView 加载窗口背景:

关键源码路径

  1. PhoneWindow.installDecor()
    初始化 DecorView 并设置背景:

    1
    2
    3
    4
    5
    6
    7
    // PhoneWindow.java
    private void installDecor() {
    if (mDecor == null) {
    mDecor = generateDecor(-1);
    mDecor.setWindowBackground(mBackgroundDrawable); // 应用windowBackground
    }
    }
  2. 主题背景的加载
    generateDecor() 中通过 Context.getTheme() 加载主题资源:

    1
    2
    3
    4
    5
    6
    7
    8
    // ContextThemeWrapper.java
    public Resources.Theme getTheme() {
    if (mTheme == null) {
    mTheme = mResources.newTheme();
    mTheme.applyStyle(mThemeResource, true); // 应用主题样式
    }
    return mTheme;
    }
    • 白屏触发点:如果未自定义 windowBackground,系统默认使用白色背景。即使自定义了背景图,若主线程耗时导致视图渲染延迟,仍会短暂显示该背景。

4. 冷启动进程初始化

冷启动时,系统需要创建应用进程并初始化 Application

关键源码路径

  1. ActivityManagerService.startProcess()
    通过 Zygote 进程 fork 新应用进程:

    1
    2
    3
    4
    // ActivityManagerService.java
    final ProcessRecord startProcess(...) {
    Process.ProcessStartResult startResult = Process.start(...);
    }
  2. ActivityThread.main()
    应用进程入口,绑定到 ActivityManagerService

    1
    2
    3
    4
    5
    6
    7
    // ActivityThread.java
    public static void main(String[] args) {
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    Looper.loop();
    }
  3. Application.onCreate()
    开发者可能在此处初始化第三方库或耗时逻辑:

    1
    2
    3
    4
    5
    // Application.java
    public void onCreate() {
    super.onCreate();
    // 耗时操作会延长冷启动时间
    }
    • 白屏触发点Application.onCreate() 中的耗时操作会延迟 Activity 的启动流程,延长白屏显示时间。

源码角度的优化策略

  1. 减少主线程阻塞

    • 避免在 Application.onCreate()Activity.onCreate() 中执行同步 I/O 或密集计算。
    • 使用异步框架(如协程、RxJava)或延迟加载。
  2. 优化窗口背景

    • 自定义 windowBackground 为启动图或品牌 Logo,提升用户体验:
      1
      2
      3
      <style name="SplashTheme" parent="Theme.AppCompat">
      <item name="android:windowBackground">@drawable/splash_bg</item>
      </style>
  3. 加速首帧渲染

    • 使用 ViewStub 或异步布局加载(如 AsyncLayoutInflater)。
    • 简化首屏布局层级,避免过度绘制。
  4. 利用 Android 12+ SplashScreen API

    • 官方 API 可统一管理启动动画,避免手动处理主题切换:
      1
      2
      3
      4
      5
      6
      7
      class MainActivity : AppCompatActivity() {
      override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      val splashScreen = installSplashScreen()
      // 延迟加载内容
      }
      }

总结

从源码角度看,白屏的根源在于 窗口背景的默认显示与首帧渲染的延迟。系统在 Activity 启动的早期阶段(onCreate() 完成前)会先绘制窗口背景,而主线程的耗时操作会阻塞视图的及时渲染,导致白屏时间延长。通过优化主题、减少主线程负载、加速首帧渲染,可显著改善这一问题。

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