Android10 Android TV Launcher(ATV) 启动时间优化记录

  • 为什么要优化?        

  • 都是ATV的情况下,H313的开机到桌面时间耗时40S左右,而且开机动画结束后会黑屏很多秒(10S)左右。

  • 同一个板子,同一个主控的情况下,ATV Launcher的启动时间比自定义的Launcher启动时间久。同样开机动画结束后会黑屏一段时间,而自定义的Launcher开机动画结束后马上就出现了。

  • 下面是数据表格:

ATV和OTT Launcher启动时间对比(时间戳数据来源于Log)
主控 H313 H616
Launcher类型 ATV OTT ATV
流程 时间戳 距离ZygoteInit的耗时(s) 时间戳 距离ZygoteInit的耗时(s)
ZygoteInit 03:00:40 0 02:33:51 0
开机动画开始 03:00:46 6 02:33:57 6
FallbackHome 03:00:58 18 未启动 0
开机动画结束 03:01:03 23 02:34:13 22
启动桌面 03:01:06 26 02:34:07 16
显示桌面 03:01:13 33 02:34:12 21
总时间(从zygoteInit开始,到显示桌面) 33 21
开机LOGO到桌面出现(秒表计时) 40S左右 30S左右 30S左右
H313 ATV优化后 30S左右
  • 分析结果:

  • 经过log分析,当桌面是ATV Launcher(com.google.android.tvlauncher)时,在启动这个launcher之前会先启动一个页面,即FallbackHome(android\packages\apps\TvSettings\Settings\src\com\android\tv\settings\system\FallbackHome.java)。它是原生Setting中的一个页面(Activity),是开机动画到锁屏解锁(即桌面正式出来)之前的一个过渡画面。

  • 当使用自定义的桌面,如果你在AndroidManifest.xml中加了android:directBootAware="true",它就可以比FallbackHome优先级高(FallbackHome也加了directBootAware,但他的优先级是-1000),也就是FallbackHome压根就不会启动,所以开机时间会短很多。ATV时启动FallbackHome的时间点和自定义Launcher启动的时间点距离ZygoteInit的时间点是接近的(大概也就是16-18s这样)。

  • FallbackHome启动后,TV版本没有锁屏服务,所以这个页面是透明或者黑色的,所以会导致开机动画结束后到桌面出现之前会黑屏一段时间。

  • 总结:也就是说,H313 ATV启动时间慢,就是因为这个FallbackHome的启动导致的。而它会比ATV启动更快就是因为它有android:directBootAware="true"和HOME属性。

  • 解决思路:

  • 1.在我们的桌面未启动之前,不结束开机动画(也就是用开机动画的最后一帧覆盖住黑屏)。缺点是开机时间并没有减短。如果你的机器没有出现黑屏这个情况,第一点可以不考虑。

  • 2.让ATV也拥有directBootAware="true"属性(从根源上解决FallbackHome启动更快的问题)。

  • 实际操作:

  • 在android\frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java中,修改这一部分代码,这一段代码修改前的作用就是开机动画结束后,刷新页面(这里之后就会进入FallbackHome,所以就黑屏了)。修改后就变成如果有锁屏的话就不那么快刷新。

            //如果有锁屏服务的话才执行这一段
            LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
            if(lockPatternUtils.isSecure(mCurrentUserId)){
                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.
                    boolean bootanim_exit_delay = mContext.getResources().getBoolean(R.bool.config_delay_exit_bootanim);
                    if (!bootanim_exit_delay)
                        SystemProperties.set("service.bootanim.exit", "1");
                    else
                        mForceDisplayEnabled = true;
                    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!");
                }
            }

            ......
  • 那改为什么时候刷新呢?我们来到android\frameworks\base\services\core\java\com\android\server\wm\ActivityRecord.java的onWindowsDrawn()这里,在能获取到home而且不是fallbackhome时进行刷新。
            .....
            if (task != null) {
                task.hasBeenVisible = true;
            }

            // 在这里刷新页面
            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!");
                }
            }
  • 这两步就能解决第一点。
  • 接下来解决第二点,就是给Launcher增加directBootAware属性。
  • 我们来到android\frameworks\base\core\java\android\content\pm\PackageParser.java的parseBaseApplication和parseActivity方法中,动态增加directBootAware属性。
private static final String ATV_PACKAGE_NAME = "com.google.android.tvlauncher";

@UnsupportedAppUsage
    private boolean parseBaseApplication(Package owner, Resources res,
            XmlResourceParser parser, int flags, String[] outError)
        throws XmlPullParserException, IOException {
        final ApplicationInfo ai = owner.applicationInfo;
        final String pkgName = owner.applicationInfo.packageName;

        ......
        if (sa.getBoolean(
                R.styleable.AndroidManifestApplication_directBootAware,
                false)) {
            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
        }

        
        //add
        if (pkgName.equals(ATV_PACKAGE_NAME)) {
            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE;
            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
        }
        //end

        ......
}


private Activity parseActivity(Package owner, Resources res,
            XmlResourceParser parser, int flags, String[] outError, CachedComponentArgs cachedArgs,
            boolean receiver, boolean hardwareAccelerated)
            throws XmlPullParserException, IOException {

            ......

            if (a.info.directBootAware) {
            owner.applicationInfo.privateFlags |=
                    ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
            }

            
            //add
            if (a.info.name.equals(ATV_PACKAGE_NAME + ".MainActivity")) {
                owner.applicationInfo.privateFlags |=
                    ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
            }
            //end

            ......
}
  • 改好后,编译固件刷机验证即可。
  • 出现的问题:

  • 1.Launcher启动后Home键无法使用,无法进入开发者选项等。

  • 2.修复1问题后,刷机后第一次开机出现按了home键Launcher再启动一次的问题(暂不清楚如何解决)。

  • 问题1原因:需要user配置的flag设置为completed。

  • 问题1解决方法:在android\frameworks\base\services\core\java\com\android\server\wm\ActivityStartController.java的startHomeActivity方法中加入以下代码:

private static final String ATV_PACKAGE_NAME = "com.google.android.tvlauncher";

void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) {
    .....
        
        if (homeStack != null && homeStack.mInResumeTopActivity) {
            // If we are in resume section already, home activity will be initialized, but not
            // resumed (to avoid recursive resume) and will stay that way until something pokes it
            // again. We need to schedule another resume.
            mSupervisor.scheduleResumeTopActivities();
        }    


    ......


        // add force skip user setup, or we can't use home key
        if (aInfo.name.equals(ATV_PACKAGE_NAME + ".MainActivity")) {
            skipUserSetup();
        }
}

// force skip user setup, or we can't use home key
private void skipUserSetup() {
        final ContentResolver resolver = mService.mContext.getContentResolver();
        if (Settings.Secure.getInt(resolver, "tv_user_setup_complete", 0) == 0) {
            Slog.d(TAG, "force skip user setup, or we can't use home key");
            Settings.Global.putInt(resolver, "device_provisioned", 1);
            Settings.Secure.putInt(resolver, "user_setup_complete", 1);
            Settings.Secure.putInt(resolver, "tv_user_setup_complete", 1);
        }
}
  • 最后

  • 感谢观看,谢谢大家。

你可能感兴趣的:(android,java)