1. 开机提示背后的系统逻辑与优化思路
1.1 从一次实际项目需求说起
前段时间接手了一个基于RK3566核心板的智能终端项目,系统跑的是Android 11,整机形态类似桌面安卓电脑,配了8寸1280×800的LVDS屏幕,4+128GB存储组合,一个USB 3.0加一个USB 2.0接口。硬件调试阶段一切顺利,LVDS点亮、触摸校准、CH340串口通信都跑通了,但到了整机交付前的体验验收环节,客户提了一个很具体的要求:开机过程中屏幕上那个“正在启动”的提示必须去掉,换成他们自己的品牌Logo动画。
这个需求听起来简单,但实际动手才发现,Android 11在RK3566/RK3568这类瑞芯微平台上,开机流程涉及U-Boot、Kernel、Init、Zygote、SystemServer多个阶段,每个阶段都有各自的显示逻辑。“正在启动”这个提示并不是某一个固定文件里的字符串,而是系统在特定启动阶段由FallbackHome机制触发的默认界面。要彻底屏蔽它,得先搞清楚它到底在哪个环节冒出来。
这篇文章就是把我整个排查和修改过程完整记录下来,包括设备树配置、FallbackHome机制分析、开机动画替换、以及最终验证的完整步骤。如果你手上有RK3566或RK3568的开发板,跑的是Android 11,也遇到了类似的开机界面定制需求,这篇内容可以直接抄作业。
1.2 为什么“正在启动”会出现在屏幕上
Android系统的启动显示大致分三个阶段。第一阶段是U-Boot阶段,这个阶段屏幕通常显示的是U-Boot的Logo或者黑屏,取决于U-Boot有没有配置开机Logo。第二阶段是Kernel启动到Init阶段,这个阶段会加载开机动画资源,也就是bootanimation.zip。第三阶段是SystemServer启动完成后,Launcher正式接管桌面之前的过渡阶段。
“正在启动”这个提示,实际上出现在第三阶段。具体来说,当SystemServer启动完成,但用户设置的Launcher还没有准备好或者没有被正确配置时,系统会启动一个叫FallbackHome的临时Home应用。这个FallbackHome的界面就是一个简单的提示文字,在中文语言环境下显示为“正在启动”,英文环境下显示为“Starting Android”。
注意:FallbackHome不是bug,它是Android系统的一个保护机制。当默认Launcher因为某些原因无法启动时,FallbackHome会临时接管,避免系统卡在黑屏状态。所以我们的目标不是删除FallbackHome,而是让它在正常启动流程中不显示出来。
1.3 整体优化方案的选型考量
针对屏蔽“正在启动”提示这个需求,市面上常见的做法有三种。第一种是直接修改FallbackHome的源码,把界面改成透明或者直接删掉。第二种是修改系统配置,让FallbackHome在启动时直接跳过显示。第三种是替换整个开机动画流程,让bootanimation一直显示到Launcher起来为止。
我最终选择的是组合方案:修改FallbackHome的显示逻辑让它透明化,同时延长bootanimation的显示时间,确保从Kernel启动到Launcher接管之间没有视觉断层。这样做的好处是不破坏系统的FallbackHome保护机制,即使Launcher启动失败,系统仍然有兜底方案,只是用户看不到那个提示界面而已。
为什么不直接删掉FallbackHome?因为在实际调试中我发现,某些情况下Launcher的启动确实会延迟,如果直接删掉FallbackHome,系统会短暂黑屏,体验反而更差。透明化处理既保留了保护机制,又满足了客户对视觉连续性的要求。
2. FallbackHome机制深度解析与修改实操
2.1 FallbackHome在Android 11中的启动流程
要改FallbackHome,先得知道它是怎么被拉起来的。在Android 11的源码中,FallbackHome的代码位于frameworks/base/services/core/java/com/android/server/am/目录下,具体文件是FallbackHome.java。这个类继承自Activity,但它的启动时机很特殊。
系统启动时,ActivityManagerService会检查当前用户是否已经设置了Home应用。如果用户没有设置,或者设置的Home应用还没有完成初始化,AMS就会启动FallbackHome作为临时Home。FallbackHome的onCreate方法里会注册一个广播接收器,监听ACTION_USER_UNLOCKED和ACTION_PACKAGE_CHANGED等广播。当真正的Launcher准备好后,FallbackHome会收到通知并调用finish()退出。
在RK3566/RK3568的Android 11 SDK中,FallbackHome的默认布局文件是fallback_home.xml,里面就是一个居中的TextView,显示的文字资源是android:string/startup_message,中文环境下就是“正在启动”。
2.2 定位FallbackHome源码与资源文件
在瑞芯微的Android 11 SDK中,FallbackHome的源码路径和AOSP基本一致,但布局文件可能会被厂商定制。我拿到的SDK里,FallbackHome的布局文件在frameworks/base/core/res/res/layout/fallback_home.xml,内容大致如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"> <TextView android:id="@+id/fallback_home_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@android:string/startup_message" android:textSize="24sp" android:textColor="@android:color/white" /> </LinearLayout>要确认你的SDK里具体路径,可以用find命令搜索:
find frameworks/base -name "fallback_home.xml" 2>/dev/null find frameworks/base -name "FallbackHome.java" 2>/dev/null如果搜不到,可能是厂商把FallbackHome的布局改到了其他位置,或者直接用了代码动态创建View。这时候需要反编译framework-res.apk来确认。
2.3 修改FallbackHome实现透明化处理
我的修改思路很简单:把FallbackHome的界面改成完全透明,同时去掉文字显示。具体操作分两步。
第一步,修改布局文件。把fallback_home.xml改成:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/transparent"> </FrameLayout>第二步,修改FallbackHome.java中的窗口属性。在onCreate方法里添加:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));这样FallbackHome启动后,屏幕上不会显示任何内容,也不会拦截触摸事件,用户完全感知不到它的存在。
实操心得:修改FallbackHome.java后,需要重新编译framework.jar。在RK3566/RK3568的Android 11 SDK中,编译命令是
make framework或者make -j8 framework。编译完成后,生成的framework.jar在out/target/product/rk3566_r/目录下,需要push到设备的/system/framework/目录。
2.4 编译与验证FallbackHome修改
编译framework.jar时有个坑要注意:RK3566和RK3568的SDK默认可能开启了预编译优化,直接make framework可能不会重新编译修改过的Java文件。我的做法是先执行make clean-framework清理,再执行make framework。
编译完成后,用adb push到设备:
adb root adb remount adb push out/target/product/rk3566_r/system/framework/framework.jar /system/framework/ adb reboot重启后观察开机过程,如果“正在启动”提示消失,说明修改生效。如果还有,可能是FallbackHome的布局被其他资源覆盖了,需要检查framework-res.apk里的资源。
3. 开机动画与启动流程的视觉衔接优化
3.1 RK3568 U-Boot阶段的开机Logo配置
屏蔽了FallbackHome的提示后,新的问题出现了:从U-Boot到Kernel再到bootanimation之间,屏幕会有短暂的黑屏。客户要求整个开机过程视觉连续,所以还得把U-Boot和Kernel阶段的显示也统一起来。
RK3568的U-Boot阶段显示Logo,需要在U-Boot的设备树中配置logo节点。具体是在u-boot/arch/arm/dts/rk3568-xxx.dts中添加:
&logo { status = "okay"; logo,uboot = "logo.bmp"; logo,kernel = "logo_kernel.bmp"; logo,mode = "center"; };其中logo.bmp是U-Boot阶段显示的图片,logo_kernel.bmp是Kernel启动阶段显示的图片。这两张图片需要放在u-boot/resource/目录下,编译时会打包进resource.img。
注意:RK3568的U-Boot Logo图片格式要求是BMP,分辨率不能超过屏幕分辨率,颜色深度支持24位和32位。我试过用1280×800的BMP图片,显示正常。
3.2 Kernel阶段的开机Logo与bootanimation衔接
Kernel启动阶段,RK3568会通过logo_kernel.bmp显示开机Logo。这个Logo会一直显示到bootanimation启动。bootanimation的启动时机是在Init阶段,由init.rc中的service bootanim触发。
为了让视觉连续,我把logo_kernel.bmp和bootanimation的第一帧做成同一张图。这样从Kernel Logo到bootanimation之间就没有视觉跳变。具体操作是:
- 准备一张1280×800的PNG图片作为bootanimation的第一帧。
- 用工具把PNG转成BMP,命名为
logo_kernel.bmp,放到U-Boot的resource目录。 - 把PNG图片放入bootanimation.zip的
part0目录,作为第一帧。
bootanimation.zip的结构如下:
bootanimation.zip ├── desc.txt ├── part0/ │ └── frame_000.png └── part1/ └── frame_001.pngdesc.txt的内容:
1280 800 30 p 1 0 part0 p 0 0 part1第一行表示分辨率1280×800,帧率30fps。第二行表示part0播放1次后停止,第三行表示part1循环播放。
3.3 延长bootanimation显示时间确保无缝过渡
默认情况下,bootanimation会在SystemServer启动完成后退出。但FallbackHome透明化后,Launcher启动可能需要额外时间,如果bootanimation退出太早,屏幕会短暂黑屏。
我的做法是修改bootanimation的退出逻辑,让它等到Launcher真正显示第一帧后再退出。具体是在frameworks/base/cmds/bootanimation/BootAnimation.cpp中,修改checkExit()函数:
bool BootAnimation::checkExit() { if (mExitPending) { return true; } // 增加判断:等待Launcher显示 if (mSystemReady && !mLauncherReady) { return false; } return false; }同时需要在SystemServer中发送一个广播,通知bootanimation Launcher已经准备好。这个修改涉及ActivityManagerService和BootAnimation的交互,代码量较大,如果不想改这么多,还有一个更简单的办法:在desc.txt中把part1的循环次数设为无限,然后在Launcher启动后手动停止bootanimation。
adb shell setprop service.bootanim.exit 1这个命令可以在Launcher的onCreate中调用,确保bootanimation在Launcher显示后才退出。
3.4 实测效果与参数调优记录
经过上述修改,我的RK3566开发板开机过程变成了:U-Boot显示品牌Logo → Kernel显示同一张Logo → bootanimation播放动画 → Launcher显示桌面。整个过程没有“正在启动”提示,也没有黑屏断层。
实测数据:从按下电源键到Launcher显示,总耗时约18秒。其中U-Boot阶段2秒,Kernel阶段3秒,bootanimation阶段10秒,Launcher启动3秒。相比修改前,总时间没有明显增加,但视觉体验提升很大。
实操心得:bootanimation的帧率不要设太高,30fps足够,设60fps会增加CPU负载,反而拖慢启动。另外,bootanimation.zip的压缩方式要用
store模式,不能用deflate,否则系统解压会消耗额外时间。
4. 常见问题排查与避坑指南
4.1 FallbackHome修改后不生效的排查思路
修改FallbackHome后最常见的问题是“改了没效果”。我遇到过几次,排查下来通常是以下几个原因。
第一,framework.jar没有真正更新。用adb shell dumpsys package查看framework的版本号,确认是否是新编译的。如果版本号没变,说明push没成功或者设备没有重启。
第二,FallbackHome的布局被overlay覆盖。有些厂商会在device/rockchip/目录下放overlay资源,覆盖了AOSP的默认布局。检查device/rockchip/rk3566/overlay/frameworks/base/core/res/res/layout/目录下有没有fallback_home.xml。
第三,FallbackHome的启动逻辑被修改。有些SDK会定制FallbackHome的启动条件,比如在ActivityManagerService.java中直接跳过了FallbackHome的启动。这时候需要检查systemReady方法中的相关代码。
4.2 开机动画不显示或显示异常的解决方法
bootanimation不显示,通常是因为desc.txt格式不对或者图片命名不规范。RK3568的bootanimation对图片命名有要求,必须是frame_%05d.png格式,比如frame_00000.png、frame_00001.png。如果命名不对,bootanimation会直接跳过。
另一个常见问题是bootanimation.zip的权限不对。在Android 11中,bootanimation.zip需要放在/system/media/目录下,权限是644,所有者是root:root。可以用以下命令检查:
adb shell ls -l /system/media/bootanimation.zip如果权限不对,用chmod 644修改。
4.3 设备树配置错误的典型表现
RK3568的设备树配置错误,最典型的表现是屏幕不亮或者显示花屏。我在调试LVDS屏幕时遇到过,原因是lvds节点的status没有设为okay,或者>&lvds { status = "okay"; >>911 { touchscreen-swapped-x-y; touchscreen-inverted-x; };
这样触摸坐标就会自动旋转,和屏幕显示方向一致。
6. 个人实操体会与后续扩展
整个项目做下来,最大的体会是:Android系统的开机流程虽然复杂,但每个阶段的显示逻辑都有明确的入口和出口。只要找准了FallbackHome这个关键点,屏蔽“正在启动”提示并不难。难的是保证整个开机过程的视觉连续性和系统稳定性,这需要反复测试和调优。
我在实际调试中还发现,不同批次的RK3566核心板,U-Boot的Logo显示可能有差异。有些批次的U-Boot默认不显示Logo,需要在编译时加上CONFIG_ROCKCHIP_LOGO配置。这个坑我踩过一次,后来在u-boot/configs/rk3566_defconfig中加上了这个配置才解决。
后续如果还要进一步优化,我打算试试把bootanimation换成视频格式,用bootvideo替代bootanimation,这样动画效果会更流畅。另外,Launcher的启动速度还有优化空间,可以通过裁剪不必要的系统应用来缩短启动时间。
最后分享一个小技巧:在调试开机流程时,可以用adb shell dmesg | grep -i "logo\|bootanim\|fallback"实时查看相关日志,快速定位问题。这个命令帮我省了不少排查时间。