白屏原因分析-源码版本
从 Android 源码角度分析 启动白屏现象,需要深入到 Activity
启动流程、窗口(Window
)管理和视图渲染机制。以下是关键源码路径及白屏产生的原因解析:
1. 冷启动流程与窗口初始化
当应用冷启动时,系统会通过 ActivityThread
的 handleLaunchActivity()
创建 Activity
实例,并触发窗口初始化:
关键源码路径:
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;
}Activity.performCreate()
执行onCreate()
,初始化布局和逻辑:1
2
3
4
5// Activity.java
final void performCreate(Bundle icicle) {
onCreate(icicle); // 开发者重写的onCreate()
// 其他初始化...
}窗口初始化与背景绘制
在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. 视图渲染与首帧延迟
即使 Activity
的 onCreate()
完成,视图的真正渲染(首帧显示)仍需通过 ViewRootImpl
和 Choreographer
完成:
关键源码路径:
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();
}
}Choreographer
与 VSYNC 信号
视图渲染依赖Choreographer
接收 VSYNC 信号后提交帧:1
2
3
4// Choreographer.java
public void postFrameCallback(FrameCallback callback) {
postFrameCallbackDelayed(callback, 0);
}- 白屏触发点:若主线程在
onCreate()
或onResume()
中有耗时操作,导致performTraversals()
延迟执行,首帧无法及时提交,系统将持续显示窗口背景(即白屏)。
- 白屏触发点:若主线程在
3. 窗口背景与主题的源码逻辑
系统通过 PhoneWindow
的 DecorView
加载窗口背景:
关键源码路径:
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
}
}主题背景的加载
在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
:
关键源码路径:
ActivityManagerService.startProcess()
通过Zygote
进程 fork 新应用进程:1
2
3
4// ActivityManagerService.java
final ProcessRecord startProcess(...) {
Process.ProcessStartResult startResult = Process.start(...);
}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();
}Application.onCreate()
开发者可能在此处初始化第三方库或耗时逻辑:1
2
3
4
5// Application.java
public void onCreate() {
super.onCreate();
// 耗时操作会延长冷启动时间
}- 白屏触发点:
Application.onCreate()
中的耗时操作会延迟Activity
的启动流程,延长白屏显示时间。
- 白屏触发点:
源码角度的优化策略
减少主线程阻塞
- 避免在
Application.onCreate()
或Activity.onCreate()
中执行同步 I/O 或密集计算。 - 使用异步框架(如协程、
RxJava
)或延迟加载。
- 避免在
优化窗口背景
- 自定义
windowBackground
为启动图或品牌 Logo,提升用户体验:1
2
3<style name="SplashTheme" parent="Theme.AppCompat">
<item name="android:windowBackground">@drawable/splash_bg</item>
</style>
- 自定义
加速首帧渲染
- 使用
ViewStub
或异步布局加载(如AsyncLayoutInflater
)。 - 简化首屏布局层级,避免过度绘制。
- 使用
利用 Android 12+ SplashScreen API
- 官方 API 可统一管理启动动画,避免手动处理主题切换:
1
2
3
4
5
6
7class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val splashScreen = installSplashScreen()
// 延迟加载内容
}
}
- 官方 API 可统一管理启动动画,避免手动处理主题切换:
总结
从源码角度看,白屏的根源在于 窗口背景的默认显示与首帧渲染的延迟。系统在 Activity
启动的早期阶段(onCreate()
完成前)会先绘制窗口背景,而主线程的耗时操作会阻塞视图的及时渲染,导致白屏时间延长。通过优化主题、减少主线程负载、加速首帧渲染,可显著改善这一问题。