开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情
在验证定制的开机动画时,我发现当开机动画执行结束后,系统首先会黑屏2s左右,然后再显示「平板电脑正在启动」的进度条2s左右,总计 4s 之后才会进入Launcher到首页。
很显然,这种用户体验是极差的。我们希望,在开机动画结束之后,就立即进入Launcher主界面,完成无缝的切换效果。
这篇文章将首先为你解疑 为什么出现黑屏? 现象,然后对症下药告诉你 如何干掉黑屏?
一、为什么出现黑屏?
首先分析下开机日志,我们定位到在进入Launcher之前,系统优先启动了 com.android.settings/com.android.settings.FallbackHome ,在 FallbackHome 停留了大约4s后,系统才准备拉起Launcher。
2023-01-18 18:02:22.536 469-469/system_process I/ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.settings/.FallbackHome} from uid 0
2023-01-18 18:02:22.558 469-469/system_process I/ActivityTaskManager: getPackageFerformanceMode--ComponentInfo{com.android.settings/com.android.settings.FallbackHome}----com.android.settings
2023-01-18 18:02:22.620 469-469/system_process I/ActivityTaskManager: getPackageFerformanceMode--ComponentInfo{com.android.settings/com.android.settings.FallbackHome}----com.android.settings
2023-01-18 18:02:22.672 469-508/system_process I/ActivityManager: Start proc 828:com.android.settings/1000 for activity {com.android.settings/com.android.settings.FallbackHome}
2023-01-18 18:02:23.125 469-506/system_process I/ActivityTaskManager: Displayed com.android.settings/.FallbackHome: +517ms
2023-01-18 18:02:26.395 828-828/com.android.settings D/FallbackHome: User unlocked and real home found; let's go!
那这个 FallbackHome 又是一个什么Activity?它的作用又是什么呢?
通过查阅相关资料发现,FallbackHome 是原生Settings应用里的一个Activity,是Android N在引入 DirectBoot Mode 功能时,新增的一个页面。
当设备已开机但用户尚未解锁设备时,Android 7.0 将在安全的“直接启动”模式下运行。默认情况下,应用不会在“直接启动”模式下运行。如果您的应用需要在“直接启动”模式下执行操作,您可以注册应在此模式下运行的应用组件。
FallbackHome 正是一个注册了可以在"直接启动"模式下运行的Activity。系统启动完成后,会优先启动 FallbackHome 直到系统完成解锁流程,才会启动Launcher。因此,FallbackHome 是一个从系统 启动完成 到 调起Launcher 的过渡界面。
开机动画正是在系统 启动完成 后退出的,所以我们会看到在开机动画结束之后,先出现了FallbackHome页面,再进入了Launcher。
那么,系统开机的时序捋清楚了:开机 -> 播放开机动画 -> 启动完成 -> 退出开机动画 -> 启动FallBackHome,同时进行解锁 -> 解锁完成 -> 调起Launcher。
关于FallbackHome的启动流程,可参考系列文章:SystemServer启动FallbackHome
二、如何干掉黑屏?
通过上小节分析出来的开机启动时序,我们找到了黑屏的原因,就是 过早结束了开机动画的播放。
如果可以在 Launcher成功启动之后,才停止开机动画的播放,从视觉效果上,就可以给用户带来的开机动画一结束就直接进入了Launcher的无缝切换。
要达到这个目的,我们需要从两个方面入手:
- 屏蔽系统默认结束开机动画的代码;
- 在Launcher启动后结束开机动画。
1. 屏蔽系统默认结束开机动画的代码
打开 frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java ,找到performEnableScreen 方法,屏蔽掉以下检测开机动画结束以及关闭开机动画的代码。
private void performEnableScreen(){
.....
/* 这里屏蔽掉一些代码
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()) {
if (DEBUG_BOOT) Slog.i(TAG_WM, "performEnableScreen: Waiting for anim complete");
return;
}
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_WM, "Boot completed: SurfaceFlinger is dead!");
}
*/
....
}
2. 在Launcher启动后结束开机动画
打开 frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java ,找到 onWindowsDrawn 方法,在其中添加代码逻辑,当 判断系统所启动的应用为Home应用但又不是FallbackHome时,结束开机动画。
onWindowsDrawn方法,顾名思义,当组件对应的Windows绘制完毕时调用,可以作为执行Activity启动完成后的逻辑注入点。
在不同版本或不同厂商的系统中,具体修改位置可能会有差异,但思路都是一致的。 笔者是基于rk3399 Android10 系统进行修改的。
public void onWindowsDrawn(boolean drawn, long timestamp) {
....
//Launcher is drawn completed,box can exit bootanim
String product = SystemProperties.get("ro.target.product");
Log.d("ActivityRecord", "ro.target.product = " + product);
// rk3399源码中已有相应代码,我这里只是把我调试设备的产品型号「tablet」也添加进了判断条件即可
if ("box".equals(product) || "tablet".equals(product)){
if(shortComponentName!=null
// 若启动的不是FallBackHome
&& !shortComponentName.contains(".FallbackHome")
// 若开机动画还未退出
&& !"1".equals(SystemProperties.get("service.bootanim.exit"))){
stopBootanim();
}
}
....
}