Android系统定制开发 | 什么?开机后居然黑屏了4秒....

745 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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的无缝切换。

要达到这个目的,我们需要从两个方面入手:

  1. 屏蔽系统默认结束开机动画的代码;
  2. 在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();
         }
    }
    ....
}