-
为什么要优化?
-
都是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);
}
}
-
最后
-
感谢观看,谢谢大家。