Android Framework 常见解决方案(24)屏蔽FallbackHome,去除 Android正在启动,直接进入Launcher

本文介绍了Android系统的FallbackHome机制,即在默认Launcher加载失败时显示的启动画面。文章详细描述了如何通过修改AOSP源码移除开机弹窗,以及在AndroidQRS中的具体修改方案,以提供更流畅的用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 原理说明

开机以后,设备会有一个“android正在启动”这样的弹框,这个界面是一个叫FallbackHome的Activity来展示的。FallbackHome机制是Android系统启动过程中的一种降级处理机制。当系统启动时,如果默认的Launcher应用无法正常加载或出现错误,系统会自动启用FallbackHome来替代默认Launcher。但一般为了改善产品体验,最终决定移除弹窗页面,直接进入默认的Launcher,避免弹窗对用户界面的干扰。具体而言,FallbackHome机制包括以下内容:

  • 默认Launcher故障检测:系统会检测默认的Launcher应用是否能够正常加载和运行。如果检测到故障,系统将启动FallbackHome。
  • 解锁过程中的弹窗:在系统解锁之前,可能会出现一个"Android正在启动"的弹窗页面,用于指示系统正在加载和准备。这个弹窗页面通常出现在桌面壁纸上,用户需要等待系统完全解锁后才能进入默认的Launcher。

去掉FallbackHome的效果实际上是让FallbackHome的view不显示,然后开机启动动画延长一下,直到系统解锁后再停止开机动画,完成这样较为顺滑的过渡。

2 修改方案(Android Q R S)

具体修改方案如下(这里以S版本修改为主,Q和R有一些差异但原理不变)

@1 修改文件为:AOSP/packages/apps/Settings$ vim src/com/android/settings/FallbackHome.java,目的为FallbackHome不显示View,所以注释掉。修改内容为:

public class FallbackHome extends Activity {
    private static final String TAG = "FallbackHome";
    private static final int PROGRESS_TIMEOUT = 2000;

    private boolean mProvisioned;
    private WallpaperManager mWallManager;

    private final Runnable mProgressTimeoutRunnable = () -> {
        //AGS add start
        //不显示任何内容,如果是黑色衔接,直接改这里就足够了,如果不是则需要 2,3步骤内容的修改
        /*  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();*/
        //AGS add end
        getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
    };
    //...
}

@2 修改文件为:AOSP/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java,目的为播放完开机动画后不退出开机动画,所以注释掉即可。修改内容为:

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    //...
    private void performEnableScreen() {
        synchronized (mGlobalLock) {
            ProtoLog.i(WM_DEBUG_BOOT, "performEnableScreen: mDisplayEnabled=%b"
                            + " mForceDisplayEnabled=%b" + " mShowingBootMessages=%b"
                            + " mSystemBooted=%b mOnlyCore=%b. %s", mDisplayEnabled,
                    mForceDisplayEnabled, mShowingBootMessages, mSystemBooted, mOnlyCore,
                    new RuntimeException("here").fillInStackTrace());
            if (mDisplayEnabled) {
                return;
            }
            if (!mSystemBooted && !mShowingBootMessages) {
                return;
            }

            if (!mShowingBootMessages && !mPolicy.canDismissBootAnimation()) {
                return;
            }

            // Don't enable the screen until all existing windows have been drawn.
            if (!mForceDisplayEnabled) {
                if (mBootWaitForWindowsStartTime < 0) {
                    // First time we will start waiting for all windows to be drawn.
                    mBootWaitForWindowsStartTime = SystemClock.elapsedRealtime();
                }
                for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
                    if (mRoot.getChildAt(i).shouldWaitForSystemDecorWindowsOnBoot()) {
                        return;
                    }
                }
                long waitTime = SystemClock.elapsedRealtime() - mBootWaitForWindowsStartTime;
                mBootWaitForWindowsStartTime = -1;
                if (waitTime > 10) {
                    ProtoLog.i(WM_DEBUG_BOOT,
                            "performEnableScreen: Waited %dms for all windows to be drawn",
                            waitTime);
                }
            }
	    //AGS add start
	    //播放完开机动画后不退出开机动画,注释掉如下代码即可。
	    /*
            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!");
            }
            */
            //AGS add end

            EventLogTags.writeWmBootAnimationDone(SystemClock.uptimeMillis());
            Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
            mDisplayEnabled = true;
            ProtoLog.i(WM_DEBUG_SCREEN_ON, "******************** ENABLING SCREEN!");

            // Enable input dispatch.
            mInputManagerCallback.setEventDispatchingLw(mEventDispatchingEnabled);
        }

        try {
            mActivityManager.bootAnimationComplete();
        } catch (RemoteException e) {
        }

        mPolicy.enableScreenAfterBoot();

	// Make sure the last requested orientation has been applied.
        updateRotationUnchecked(false, false);
    }
    //...
}

@3 修改文件为:AOSP/frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java,目的为解锁后再结束开机动画。修改内容为:

public final class ActivityRecord extends WindowToken implements WindowManagerService.AppFreezeListener {
/** Called when the windows associated app window container are drawn. */
    //...
    private void onWindowsDrawn(long timestampNs) {
        if (mPerf != null && perfActivityBoostHandler > 0) {
            mPerf.perfLockReleaseHandler(perfActivityBoostHandler);
            perfActivityBoostHandler = -1;
        } else if (perfActivityBoostHandler > 0) {
            Slog.w(TAG, "activity boost didn't release as expected");
        }
        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);
        }
        //AGS add start
        if (isHomeIntent(intent) && shortComponentName != null && !shortComponentName.contains("FallbackHome")) {
            SystemProperties.set("service.bootanim.exit", "1");
	    //这里就是将上面注掉的代码copy过来
            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!");
            }
        }
        //AGS add end
        mLaunchRootTask = null;
    }
    //...
}

### 回答1: trace.common.unicodetozpl是一个函数,用于将Unicode编码转换成ZPL(Zebra打印标签语言)格式。Unicode是一种字符编码系统,用于表示世界上所有的语言和字符,而ZPL是一种标签制作语言,用于打印标签和条形码。 该函数可以帮助开发人员将Unicode编码的字符串转换成适用于Zebra打印机的ZPL格式。通过这种方式,开发人员可以将Unicode编码的文字和符号打印在标签上,从而实现多语言的标签打印。 在实际应用中,开发人员可以将Unicode编码的信息存储在数据库中或从其他系统中获取,然后使用trace.common.unicodetozpl函数将其转换成ZPL格式,并发送给Zebra打印机进行打印。这样就可以实现在同一张标签上打印多种语言和符号的功能,提高了标签打印的效率和准确性。 总之,trace.common.unicodetozpl函数为开发人员提供了一个简单而有效的方法,将Unicode编码的信息转换成适用于Zebra打印机的ZPL格式,实现多语言的标签打印。 ### 回答2: trace.common.unicodetozpl 是一个将 Unicode 编码转化为 ZPL 格式的转化器。Unicode编码是一种文本编码方式,它将各种字符以数字来表示,它可以通过网络、电子邮件或软件之间的通讯传输,并能够良好地支持多种语言的编写。而ZPL格式则是一个由软件推出的打印机语言,用于打印条形码、标签等等。 传统的条码和标签打印机需要安装复杂的驱动程序和软件来打印条码和标签。而使用 trace.common.unicodetozpl 转化器可以方便快捷地将 Unicode 编码转化为ZPL格式,使打印设备轻松地与 Unicode 编码兼容,从而可以更简单地打印标签和条形码,提高了操作的便利性和效率。 此外, trace.common.unicodetozpl 转化器还支持生成多个标签,以及导入外部数据源等功能。这使得标签的打印更加自由和便捷,大大提高了生产效率和准确性。 ### 回答3: trace.common.unicodetozpl是一种将Unicode编码转换为ZPL格式的方法或代码片段。Unicode编码是一种标准字符编码,用于在计算机上存储和传输文本。而ZPL则是打印机控制语言,用于控制标签打印机的打印内容和格式。通过使用trace.common.unicodetozpl代码片段,我们可以将Unicode编码的文本转换为ZPL格式,以便在标签打印机上打印出需要的文本内容。这在商业和工业领域中应用广泛,例如制造业特别是物流领域,需要打印出一系列标签来跟踪物流信息。使用这种方法,可以大大提高打印效率和精确度,同时还可以确保文本的正确性和格式化。对于需要大量打印的企业和组织来说,trace.common.unicodetozpl是一个非常实用的工具和技术,可以为他们带来诸多便利和好处。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图王大胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值