在 Android 系统开发中,开机体验是衡量设备流畅度与用户满意度的关键指标之一。默认的 Android 11 系统在启动过程中会短暂显示“系统启动中”或“Android 正在启动”的过渡界面(FallbackHome),这不仅打断了开机动画的连贯性,还可能让用户误以为系统卡顿。本文将深入探讨如何通过修改 Framework 层代码,屏蔽这一过渡界面,并延迟退出开机动画,直到真正的 Launcher 绘制完成,从而实现从开机动画到桌面的一镜到底效果。
1. 延迟退出开机动画,等待 Launcher 绘制完成
Android 系统的开机流程中,SurfaceFlinger 负责合成并显示开机动画。默认情况下,系统会在 Launcher 进程启动后立即结束动画,但此时 Launcher 可能尚未完成首次绘制,导致用户看到短暂的空白或过渡界面。优化的核心思路是:将结束开机动画的时机从“Launcher 启动”推迟到“Launcher 首次绘制完成”。
具体实现涉及修改 WindowManagerService 中的关键方法。文件路径如下:
frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
修改说明:在 onWindowsDrawn 方法的末尾添加逻辑,当检测到绘制完成的窗口是真正的 Launcher(排除 FallbackHome)时,才通知系统和 SurfaceFlinger 结束开机动画。代码修改如下:
/** Called when the windows associated app window container are drawn. */
private void onWindowsDrawn(long timestampNs) {
final TransitionInfoSnapshot info = mTaskSupervisor
.getActivityMetricsLogger().notifyWindowsDrawn(this, timestampNs);
final boolean validInfo = info != null;
final int windowsDrawnDelayMs = validInfo ? info.windowsDrawnDelayMs : INVALID_DELAY;
final @WaitResult.LaunchState int launchState =
validInfo ? info.getLaunchState() : WaitResult.LAUNCH_STATE_UNKNOWN;
// The activity may have been requested to be invisible (another activity has been launched)
// so there is no valid info. But if it is the current top activity (e.g. sleeping), the
// invalid state is still reported to make sure the waiting result is notified.
if (validInfo || this == getDisplayArea().topRunningActivity()) {
mTaskSupervisor.reportActivityLaunched(false /* timeout */, this,
windowsDrawnDelayMs, launchState);
}
finishLaunchTickingLocked();
if (task != null) {
task.setHasBeenVisible(true);
}
// --- 新增代码开始 ---
// 说明:解锁后退出开机动画(等待真正的 Home 绘制完成)
if (isHomeIntent(intent) && shortComponentName != null && !shortComponentName.contains("FallbackHome")) {
SystemProperties.set("service.bootanim.exit", "1");
android.util.Log.e("ActivityRecord", "real home....." + shortComponentName);
try {
IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
if (surfaceFlinger != null) {
Slog.i(TAG_WM, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
Parcel data = Parcel.obtain();
data.writeInterfaceToken("android.ui.ISurfaceComposer");
surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
data, null, 0);
data.recycle();
}
} catch (RemoteException ex) {
Slog.e(TAG, "Boot completed: SurfaceFlinger is dead!");
}
}
// --- 新增代码结束 ---
} 技术要点: 这一修改利用了 Android 的窗口绘制回调机制。在 Java 层,WindowManagerService 通过监听窗口的 finishedDrawing 事件来判断绘制状态。类似地,在 JavaScript 或 TypeScript 的前端开发中,也有 requestAnimationFrame 或 MutationObserver 来监听 DOM 元素的渲染完成。虽然语言和平台不同,但“延迟响应直到渲染就绪”的设计模式是相通的。在 Android Framework 的 C++ 层(如 SurfaceFlinger),同样需要同步处理这一信号,确保动画结束与界面绘制完全同步。
⚙️ 2. 移除原有的开机动画退出逻辑
在默认代码中,ActivityManagerService 会在 Launcher 进程启动时主动触发结束开机动画的逻辑。既然我们已经将结束时机延后到了 Launcher 绘制完成,就必须移除原有的提前退出逻辑,避免冲突。
文件路径如下:
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
修改说明:去除 performEnableScreen 方法中原有的结束开机动画相关代码(主要是注释掉对 SurfaceFlinger 的 BOOT_FINISHED 通知以及设置属性的逻辑)。因为我们已经在步骤 1 中将其延后到了 Launcher 首次绘制完成后执行。代码修改如下:
// Don't enable the screen until all existing windows have been drawn.
if (!mForceDisplayEnabled) {
// ... 省略部分原有代码
}
/* --- 注释掉以下原生逻辑(此部分逻辑已移至 ActivityRecord 处理) ---
if (!mBootAnimationStopped) {
Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
// stop boot animation
// formerly we would just kill the process, but we now ask it to exit so it
// can choose where to stop the animation.
SystemProperties.set("service.bootanim.exit", "1");
mBootAnimationStopped = true;
}
if (!mForceDisplayEnabled && !checkBootAnimationCompleteLocked()) {
ProtoLog.i(WM_DEBUG_BOOT, "performEnableScreen: Waiting for anim complete");
return;
}
try {
IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
if (surfaceFlinger != null) {
ProtoLog.i(WM_ERROR, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
Parcel data = Parcel.obtain();
data.writeInterfaceToken("android.ui.ISurfaceComposer");
surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
data, null, 0);
data.recycle();
}
} catch (RemoteException ex) {
ProtoLog.e(WM_ERROR, "Boot completed: SurfaceFlinger is dead!");
}
--- 注释结束 --- */⚠️ 注意事项: 在移除这部分逻辑时,务必确认没有其他模块依赖该通知。例如,某些第三方 ROM 或系统应用可能通过监听 BOOT_ANIMATION_DONE 属性来执行初始化任务。如果直接注释掉,可能导致这些任务无法正常触发。建议在修改前使用 Python 脚本或 grep 工具全局搜索相关属性名,评估影响范围。同时,在 Java 层修改时,注意保持代码的健壮性,使用 if (DEBUG) 等条件编译开关来方便后续调试。
3. 屏蔽 FallbackHome(系统启动中界面)
FallbackHome 是 Android 系统在 Launcher 尚未就绪时显示的过渡 Activity。它的出现会打断开机动画的连贯性,给用户一种“系统重启了”的错觉。优化的目标就是完全屏蔽这个界面。
文件路径如下:
packages/apps/Settings/src/com/android/settings/FallbackHome.java
修改说明:注释掉 mProgressTimeoutRunnable 线程中显示 “系统启动中(FallbackHome)” 界面的 View 动画逻辑,避免在开机底层准备阶段以及等待 Launcher 加载的过程中闪现该过渡视图。代码修改如下:
private final Runnable mProgressTimeoutRunnable = () -> {
/* --- 注释掉加载过渡界面的代码 ---
View v = getLayoutInflater().inflate(
R.layout.fallback_home_finishing_boot, null);
setContentView(v);
v.setAlpha(0f);
v.animate()
.alpha(1f)
.setDuration(500)
.setInterpolator(AnimationUtils.loadInterpolator(
this, android.R.interpolator.fast_out_slow_in))
.start();
--- 注释结束 --- */
getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
}; 实践技巧: 在屏蔽 FallbackHome 的同时,建议在 Launcher 的 onCreate 方法中添加一个快速加载的占位布局(例如显示品牌 Logo 或进度条),以避免用户看到黑屏或白屏。这种“占位内容”的设计思路在 Web 开发中也很常见,比如使用 JavaScript 的 loading 状态管理库(如 React 的 Suspense)来优化首屏加载体验。此外,如果你在 TypeScript 项目中使用了 Python 构建工具(如 Bazel 或 Buck),也可以借鉴这里的“延迟显示”思想,优化构建过程中的 UI 反馈。
4. 综合调试与验证
完成上述三处修改后,需要进行系统性的验证。建议的测试流程如下:
- 冷启动测试: 完全关机后开机,观察开机动画是否持续到 Launcher 图标完全显示后才结束。
- 热重启测试: 通过
adb shell reboot或系统设置中的重启功能,验证修改在软重启场景下的表现。 - 异常场景测试: 模拟 Launcher 崩溃或加载超时(例如通过
am force-stop强制停止 Launcher),检查系统是否会回退到 FallbackHome 或显示错误提示。 - 日志分析: 使用
logcat -b events过滤boot_progress相关日志,确认各阶段的时序是否符合预期。
如果遇到开机动画结束后屏幕卡死的情况,请优先检查步骤 1 中的 finishedDrawing 回调是否被正确触发。可以使用 Java 的 StrictMode 或 C++ 的 ScopedTrace 来跟踪方法调用栈。另外,建议在修改前使用 Git 等版本管理工具创建分支,方便回退和对比。
总结与最佳实践
本文通过三个关键修改点——延迟结束动画、移除提前退出逻辑、屏蔽 FallbackHome——实现了 Android 11 开机动画与 Launcher 的无缝衔接。这套优化方案不仅适用于原生 AOSP 项目,也适用于基于 Android 11 的定制 ROM 或车机系统。在实际项目中,建议结合设备的具体硬件性能(如 CPU 频率、内存大小)来调整超时阈值,避免在低端设备上因 Launcher 加载过慢而导致用户等待过久。此外,这套“延迟响应直到渲染就绪”的设计模式,可以推广到其他需要优化启动体验的场景,例如应用冷启动优化、WebView 预加载等。希望本文能为你优化 Android 开机体验提供有价值的参考。
[AFFILIATE_SLOT_2]
浙公网安备 33010602011771号