SplashScreen功能从android12开始添加,本文章分析了SplashScreen的启动流程,整体和独立App移除方案以及客制化
在开发者模式将Animation scale调成10x,adb shell输入命令:
1 | dumpsys window windows |
获取结果,以下为Splash Screen对应的Window信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | Window #8 Window{9d4659b u0 Splash Screen com.google.android.gm}: mDisplayId=0 rootTaskId=12 mSession=Session{3fbadb0 1074:u0a10116} mClient=android.os.BinderProxy@d8c05aa mOwnerUid=10116 showForAllUsers= true package=com.google.android.gm appop=NONE mAttrs={(0,0)(fillxfill) sim={adjust=pan} ty=APPLICATION_STARTING fmt =TRANSLUCENT wanim=0x7f1603e6 fl=NOT_FOCUSABLE NOT_TOUCHABLE LAYOUT_IN_SCREEN LAYOUT_INSET_DECOR ALT_FOCUSABLE_IM HARDWARE_ACCELERATED DRAWS_SYSTEM_BAR_BACKGROUNDS pfl=SHOW_FOR_ALL_USERS USE_BLAST APPEARANCE_CONTROLLED FIT_INSETS_CONTROLLED apr=LIGHT_STATUS_BARS LIGHT_NAVIGATION_BARS bhv=DEFAULT fitSides=} Requested w=3840 h=2160 mLayoutSeq=1595 mBaseLayer=21000 mSubLayer=0 mToken=ActivityRecord{2e6ad9e u0 com.google.android.gm/.ConversationListActivityGmail} t12} mActivityRecord=ActivityRecord{2e6ad9e u0 com.google.android.gm/.ConversationListActivityGmail} t12} mAppDied= false drawnStateEvaluated= true mightAffectAllDrawn= true mViewVisibility=0x0 mHaveFrame= true mObscured= false mGivenContentInsets=[0,0][0,0] mGivenVisibleInsets=[0,0][0,0] mFullConfiguration={1.0 ?mcc?mnc [en_US] ldltr sw720dp w1280dp h696dp 480dpi lrg long land finger qwerty /v/v dpad /v winConfig={ mBounds=Rect(0, 0 - 3840, 2160) mAppBounds=Rect(0, 0 - 3840, 2160) mMaxBounds=Rect(0, 0 - 3840, 2160) mDisplayRotation=ROTATION_0 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.1 fontWeightAdjustment=0} mLastReportedConfiguration={1.0 ?mcc?mnc [en_US] ldltr sw720dp w1280dp h696dp 480dpi lrg long land finger qwerty /v/v dpad /v winConfig={ mBounds=Rect(0, 0 - 3840, 2160) mAppBounds=Rect(0, 0 - 3840, 2160) mMaxBounds=Rect(0, 0 - 3840, 2160) mDisplayRotation=ROTATION_0 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} s.1 fontWeightAdjustment=0} mHasSurface= true isReadyForDisplay()= true mWindowRemovalAllowed= false Frames: parent=[0,0][3840,2160] display=[0,0][3840,2160] frame=[0,0][3840,2160] last=[0,0][3840,2160] insetsChanged= false surface=[0,0][0,0] WindowStateAnimator{28bc62d Splash Screen com.google.android.gm}: mSurface=Surface(name=Splash Screen com.google.android.gm)/@0xfb34762 Surface: shown= true layer=0 alpha=1.0 rect=(0.0,0.0) transform=(1.0, 0.0, 0.0, 1.0) mDrawState=HAS_DRAWN mLastHidden= false mEnterAnimationPending= false mSystemDecorRect=[0,0][0,0] mForceSeamlesslyRotate= false seamlesslyRotate: pending=null isOnScreen= true isVisible= true keepClearAreas: restricted=[], unrestricted=[] |
以上获取window名字关键字Splash Screen,在整机代码库framework/base目录下搜索:
grep -nr "Splash Screen"
搜索结果:根据搜索结果,定位为framework/base/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StaringSurfaceDrawer.java 311行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /** * Called when a task need a splash screen starting window. * * @param suggestType The suggestion type to draw the splash screen. */ void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, IBinder appToken, @StartingWindowType int suggestType) { ... params.setTitle( "Splash Screen " + activityInfo.packageName); // TODO(b/173975965) tracking performance // Prepare the splash screen content view on splash screen worker thread in parallel, so the // content view won't be blocked by binder call like addWindow and relayout. // 1. Trigger splash screen worker thread to create SplashScreenView before/while // Session#addWindow. // 2. Synchronize the SplashscreenView to splash screen thread before Choreographer start // traversal, which will call Session#relayout on splash screen thread. // 3. Pre-draw the BitmapShader if the icon is immobile on splash screen worker thread, at // the same time the splash screen thread should be executing Session#relayout. Blocking the // traversal -> draw on splash screen thread until the BitmapShader of the icon is ready. // Record whether create splash screen view success, notify to current thread after // create splash screen view finished. |
通过上面代码可以看出在addSplashScreenStaringWindow方法内调用的,用frida打印拦截调用栈,hook脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | function printStack() { console.warn(Java.use( "android.util.Log" ).getStackTraceString(Java.use( "java.lang.Throwable" ).$ new ())); } Java.perform( function () { //先枚举出classloader,用正确的classloader加载StrartingSurfaceDrawer Java.enumerateClassLoaders({ onMatch: function (loader) { try { var SystemUIClass = loader.loadClass( "com.android.wm.shell.startingsurface.StartingSurfaceDrawer" ); console.log( "-----Found SystemUI ClassLoader:" , loader); hookSystemUIClass(loader); } catch (e) { console.log( "------not right ClassLoader:" , loader, e); } }, onComplete: function () {} }); function hookSystemUIClass(classLoader) { console.log( "hook start ..." ); var Drawer = Java.use( "com.android.wm.shell.startingsurface.StartingSurfaceDrawer" , { 'loader' : classLoader }); Drawer.addSplashScreenStartingWindow.overload( "android.window.StartingWindowInfo" , "android.os.IBinder" , "int" ).implementation = function (windowInfo, iBinder, suggestType) { console.log( "called addSplashScreenStartingWindow" , windowInfo, iBinder, suggestType); printStack(); this .addSplashScreenStartingWindow(windowInfo, iBinder, suggestType); }; } }); |
上面脚本需要注意的是使用Java.enumerateClassLoaders方法枚举了类加载器,并尝试加载StartingSurfaceDrawer,如果加载失败说明该加载器加载不到StartingSurfaceDrawer类,加载成功就使用该加载器指定Java.use,否则执行脚本会报:java.lang.ClassNotFoundException: com.android.wm.shell.startingsurface.StartingSurfaceDrawer错误。
接下来运行脚本:
1 2 3 4 5 6 | . /frida -p `pidof com.android.systemui` -s a.js < ------not right ClassLoader: dalvik.system.PathClassLoader[DexPathList[[directory "." ],nativeLibraryDirectories=[ /system/lib64 , /system_ext/lib64 , /system/lib64 , /system_ext/lib64 ]]] Error: java.lang.ClassNotFoundException: Didn't find class "com.android.wm.shell.startingsurface.StartingSurfaceDrawer" on path: DexPathList[[directory "." ],nativeLibraryDirectories=[ /system/lib64 , /system_ext/lib64 , /system/lib64 , /system_ext/lib64 ]] ------not right ClassLoader: java.lang.BootClassLoader@bad296a Error: java.lang.ClassNotFoundException: com.android.wm.shell.startingsurface.StartingSurfaceDrawer -----Found SystemUI ClassLoader: dalvik.system.PathClassLoader[DexPathList[[zip file "/system_ext/priv-app/SystemUI/SystemUI.apk" ],nativeLibraryDirectories=[ /system_ext/priv-app/SystemUI/lib/arm64 , /system/lib64 , /system_ext/lib64 , /system/lib64 , /system_ext/lib64 ]]] hook start ... ------not right ClassLoader: dalvik.system.PathClassLoader[DexPathList[[],nativeLibraryDirectories=[ /system_ext/priv-app/SetupWizard/lib/arm64 , /system_ext/priv-app/SetupWizard/SetupWizard .apk! /lib/arm64-v8a , /system/lib64 , /system_ext/lib64 , /system/lib64 , /system_ext/lib64 ]]] Error: java.lang.ClassNotFoundException: Didn't find class "com.android.wm.shell.startingsurface.StartingSurfaceDrawer" on path: DexPathList[[],nativeLibraryDirectories=[ /system_ext/priv-app/SetupWizard/lib/arm64 , /system_ext/priv-app/SetupWizard/SetupWizard .apk! /lib/arm64-v8a , /system/lib64 , /system_ext/lib64 , /system/lib64 , /system_ext/lib64 ]] |
冷启动应用,终端输出调用栈信息:
1 2 3 4 5 6 7 8 9 10 11 | called addSplashScreenStartingWindow StartingWindowInfo{taskId=10 targetActivityInfo=null displayId=0 topActivityType=1 preferredStartingWindowType=8b insetsState=null topWindowLayoutParams=null mainWindowLayoutParams=null splashScreenThemeResId 7f150346 [object Object] 1 java.lang.Throwable at com.android.wm.shell.startingsurface.StartingSurfaceDrawer.addSplashScreenStartingWindow(Native Method) at com.android.wm.shell.startingsurface.StartingWindowController.lambda$addStartingWindow$0(StartingWindowController.java:131) at com.android.wm.shell.startingsurface.StartingWindowController.$r8$lambda$PSvN_iYbAze6X2c1OXkMhBsWeHo(Unknown Source:0) at com.android.wm.shell.startingsurface.StartingWindowController$$ExternalSyntheticLambda0.run(Unknown Source:6) at android.os.Handler.handleCallback(Handler.java:942) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.os.HandlerThread.run(HandlerThread.java:67) |
从以上堆栈信息可以知道,StartingSurfaceDrawer的addSplashScreenStartingWindow是在com.android.wm.shell.startingsurface.StartingWindowController的addStartingWindow的匿名函数中执行的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | /** * Called when a task need a starting window. */ public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { mSplashScreenExecutor.execute(() -> { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addStartingWindow" ); ... if (isSplashScreenType(suggestionType)) { mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken, suggestionType); } else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) { final TaskSnapshot snapshot = windowInfo.taskSnapshot; mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, snapshot); } if (suggestionType != STARTING_WINDOW_TYPE_NONE) { int taskId = runningTaskInfo.taskId; int color = mStartingSurfaceDrawer .getStartingWindowBackgroundColorForTask(taskId); if (color != Color.TRANSPARENT) { synchronized (mTaskBackgroundColors) { mTaskBackgroundColors.append(taskId, color); } } if (mTaskLaunchingCallback != null && isSplashScreenType(suggestionType)) { mTaskLaunchingCallback.accept(taskId, suggestionType, color); } } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); }); } |
hook addStartingWindow调用栈:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | function printStack() { console.warn(Java.use( "android.util.Log" ).getStackTraceString(Java.use( "java.lang.Throwable" ).$ new ())); } Java.perform( function () { Java.enumerateClassLoaders({ onMatch: function (loader) { try { var SystemUIClass = loader.loadClass( "com.android.wm.shell.startingsurface.StartingWindowController" ); console.log( "-----Found SystemUI ClassLoader:" , loader); hookSystemUIClass(loader); } catch (e) { console.log( "------not right ClassLoader:" , loader, e); } }, onComplete: function () {} }); function hookSystemUIClass(classLoader) { console.log( "hook start ..." ); var controller = Java.use( "com.android.wm.shell.startingsurface.StartingWindowController" , { 'loader' : classLoader }); controller.addStartingWindow.overload( "android.window.StartingWindowInfo" , "android.os.IBinder" ).implementation = function (windowInfo, apptoken) { console.log( "called StartingWindowController addStartingWindow" , windowInfo, apptoken); printStack(); this .addStartingWindow(windowInfo, apptoken); }; } }); |
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | called StartingWindowController addStartingWindow StartingWindowInfo{taskId=14 targetActivityInfo=null displayId=0 topActivityType=1 preferredStartingWindowType=8b insetsState=null topWindowLayoutParams=null mainWindowLayoutParams=null splashScreenThemeResId 7f1504d4 [object Object] java.lang.Throwable at com.android.wm.shell.startingsurface.StartingWindowController.addStartingWindow(Native Method) at com.android.wm.shell.ShellTaskOrganizer.addStartingWindow(ShellTaskOrganizer.java:392) at android.window.TaskOrganizer$1.lambda$addStartingWindow$0$android-window-TaskOrganizer$1(TaskOrganizer.java:286) at android.window.TaskOrganizer$1$$ExternalSyntheticLambda5.run(Unknown Source:6) at android.os.Handler.handleCallback(Handler.java:942) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.app.ActivityThread.main(ActivityThread.java:7905) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:956) |
调用栈看StartingWindowController的addStartingWindow实际上是ShellTaskOrganizer通过调用addStartingWindow调用。ShellTaskOrganizer继承自android.Window.TaskOrganizer,addStartingWindow方法也重写自TaskOrganizer,TaskOrganizer内的匿名内部类示例调用了TaskOrganizer的addStartingWindow方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | public class TaskOrganizer extends WindowOrganizer { private final ITaskOrganizerController mTaskOrganizerController; /** * Register a TaskOrganizer to manage tasks as they enter a supported windowing mode. * * @return a list of the tasks that should be managed by the organizer, not including tasks * created via {@link #createRootTask}. */ @RequiresPermission (android.Manifest.permission.MANAGE_ACTIVITY_TASKS) @CallSuper @NonNull public List<TaskAppearedInfo> registerOrganizer() { try { return mTaskOrganizerController.registerTaskOrganizer(mInterface).getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } ... private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() { @Override public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { mExecutor.execute(() -> TaskOrganizer. this .addStartingWindow(windowInfo, appToken)); } @Override public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { mExecutor.execute(() -> TaskOrganizer. this .removeStartingWindow(removalInfo)); } .... } } |
不难看出 mIterface涉及了跨进程调用,在TaskOrganizer的registerOrganizer方法内向服务端注册。在SystemUI启动的时候在ShellInitImpl的init方法内调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public class ShellInitImpl{ public ShellInit asShellInit() { return mImpl; } private void init() { ... // Setup the shell organizer mShellTaskOrganizer.initStartingWindow(mStartingWindow); mShellTaskOrganizer.registerOrganizer(); ... } @ExternalThread private class InitImpl implements ShellInit { @Override public void init() { try { mMainExecutor.executeBlocking(() -> ShellInitImpl. this .init()); } catch (InterruptedException e) { throw new RuntimeException( "Failed to initialize the Shell in 2s" , e); } } } } |
接着在服务端framework/base/services搜索addStartingWindow跨进程方法:
$ grep -nr "addStartingWindow"... core/java/com/android/server/wm/ActivityRecord.java:2297: boolean addStartingWindow(String pkg, int resolvedTheme, ActivityRecord from, boolean newTask, core/java/com/android/server/wm/ActivityRecord.java:6982: final boolean scheduled = addStartingWindow(packageName, resolvedTheme, core/java/com/android/server/wm/KeyguardController.java:267: mRootWindowContainer.addStartingWindowsForVisibleActivities(); core/java/com/android/server/wm/RootWindowContainer.java:2613: void addStartingWindowsForVisibleActivities() { core/java/com/android/server/wm/StartingSurfaceController.java:85: if (task != null && mService.mAtmService.mTaskOrganizerController.addStartingWindow( core/java/com/android/server/wm/StartingSurfaceController.java:169: mService.mAtmService.mTaskOrganizerController.addStartingWindow(task, core/java/com/android/server/wm/TaskOrganizerController.java:493: boolean addStartingWindow(Task task, ActivityRecord activity, int launchTheme, core/java/com/android/server/wm/TaskOrganizerController.java:510: lastOrganizer.addStartingWindow(info, activity.token);
不难看出服务端调用了 core/java/com/android/server/wm/TaskOrganizerController.java 510行 lastOrganizer.addStartingWindow(info, activity.token),改行代码位于TaskOrganizerController的addStartingWindow方法内:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | boolean addStartingWindow(Task task, ActivityRecord activity, int launchTheme, TaskSnapshot taskSnapshot) { final Task rootTask = task.getRootTask(); if (rootTask == null || activity.mStartingData == null) { return false ; } final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast(); if (lastOrganizer == null) { return false ; } final StartingWindowInfo info = task.getStartingWindowInfo(activity); if (launchTheme != 0) { info.splashScreenThemeResId = launchTheme; } info.taskSnapshot = taskSnapshot; // make this happen prior than prepare surface try { lastOrganizer.addStartingWindow(info, activity.token); } catch (RemoteException e) { Slog.e(TAG, "Exception sending onTaskStart callback" , e); return false ; } return true ; } |
hook TaskOrganizerController addStartingWindow的调用栈:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | function printStack() { console.warn(Java.use( "android.util.Log" ).getStackTraceString(Java.use( "java.lang.Throwable" ).$ new ())); } Java.perform( function () { hookStart(); function hookStart() { console.log( "hook start ..." ); var TaskOrganizerController = Java.use( "com.android.server.wm.TaskOrganizerController" ); TaskOrganizerController.addStartingWindow.overload( "com.android.server.wm.Task" , "com.android.server.wm.ActivityRecord" , "int" , "android.window.TaskSnapshot" ).implementation = function (task,record,theme,snapshot) { console.log( "called addStartingWindow" ,task,record,theme,snapshot); printStack(); let result = this .addStartingWindow(task,record,theme,snapshot); console.log( "----addStartingWindow result" , result) return result; }; } }); |
执行调用栈结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /frida-inject -p `pidof system_server` -s d.js < hook start ... called addStartingWindow Task{c46814 #99 type=standard A=10063:com.google.android.youtube.tv U=0 visible=true mode=fullscreen translucent=true sz=1} ActivityRecord{3f31267 u0 com.google.android.youtube.tv/com.google.android.apps.youtube.tv.activity.ShellActivity t99} 2132083066 null java.lang.Throwable at com.android.server.wm.TaskOrganizerController.addStartingWindow(Native Method) at com.android.server.wm.StartingSurfaceController.createSplashScreenStartingSurface(StartingSurfaceController.java:71) at com.android.server.wm.SplashScreenStartingData.createStartingSurface(SplashScreenStartingData.java:56) at com.android.server.wm.ActivityRecord$AddStartingWindow.run(ActivityRecord.java:2071) at com.android.server.wm.ActivityRecord.scheduleAddStartingWindow(ActivityRecord.java:2032) at com.android.server.wm.ActivityRecord.addStartingWindow(ActivityRecord.java:2015) at com.android.server.wm.ActivityRecord.showStartingWindow(ActivityRecord.java:6419) at com.android.server.wm.Task.startActivityLocked(Task.java:6785) at com.android.server.wm.ActivityStarter.startActivityInner(ActivityStarter.java:1818) at com.android.server.wm.ActivityStarter.startActivityUnchecked(ActivityStarter.java:1608) at com.android.server.wm.ActivityStarter.executeRequest(ActivityStarter.java:1196) at com.android.server.wm.ActivityStarter.execute(ActivityStarter.java:678) at com.android.server.wm.ActivityTaskManagerService.startActivityAsUser(ActivityTaskManagerService.java:1201) at com.android.server.wm.ActivityTaskManagerService.startActivityAsUser(ActivityTaskManagerService.java:1173) at com.android.server.wm.ActivityTaskManagerService.startActivity(ActivityTaskManagerService.java:1148) at android.app.IActivityTaskManager$Stub.onTransact(IActivityTaskManager.java:869) at com.android.server.wm.ActivityTaskManagerService.onTransact(ActivityTaskManagerService.java:5040) at android.os.Binder.execTransactInternal(Binder.java:1179) at android.os.Binder.execTransact(Binder.java:1143) ----addStartingWindow result true |
从调用栈可以看出,服务端添加SplashScreen是在启动Activity流程调用的。
上面已经梳理了SplashScreen的添加流程,那如何移除SplashScreen呢?
回到StaringWindowController的addStartingWindow方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { mSplashScreenExecutor.execute(() -> { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addStartingWindow" ); final int suggestionType = mStartingWindowTypeAlgorithm.getSuggestedWindowType( windowInfo); final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo; if (isSplashScreenType(suggestionType)) { mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken, suggestionType); } else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) { final TaskSnapshot snapshot = windowInfo.taskSnapshot; mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, snapshot); } if (suggestionType != STARTING_WINDOW_TYPE_NONE) { int taskId = runningTaskInfo.taskId; int color = mStartingSurfaceDrawer .getStartingWindowBackgroundColorForTask(taskId); if (color != Color.TRANSPARENT) { synchronized (mTaskBackgroundColors) { mTaskBackgroundColors.append(taskId, color); } } if (mTaskLaunchingCallback != null && isSplashScreenType(suggestionType)) { mTaskLaunchingCallback.accept(taskId, suggestionType, color); } } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); }); } private static boolean isSplashScreenType(@StartingWindowType int suggestionType) { return suggestionType == STARTING_WINDOW_TYPE_SPLASH_SCREEN || suggestionType == STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN || suggestionType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN; } |
通过上面代码分析,当mStartingWindowTypeAlgorithm.getSuggestedWindowType返回值类型是STARTING_WINDOW_TYPE_SPLASH_SCREEN、STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN或STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN时,会调用StartingSurfaceDrawer的addSplashScreen方法去添加SplashScreenView。当返回类型为STARTING_WINDOW_TYPE_SNAPSHOT时会添加一个快照窗口。 当反回类型为STARTING_WINDOW_TYPE_NONE时,不会调用StarintSurfaceDrawer的添加方法,因此,只要让mStaringWindowTypeAlgorithm.getSuggestedWindowType返回类型为STARTING_WINDOW_TYPE_NONE就可以整体移除Splash Screen窗口。
实际上,在android TV上,mStartingWindowTypeAlgorithm是TvStartingWindowTypeAlgorithm示例,其getSuggestedWindowType返回值在android 13和14一些版本中已经被设置成了STARTING_WINDOW_TYPE_NONE:
1 2 3 4 5 6 7 8 9 10 11 | /** * Algorithm for determining the type of a new starting window on Android TV. * For now we do not want to show any splash screens on Android TV. */ public class TvStartingWindowTypeAlgorithm implements StartingWindowTypeAlgorithm { @Override public int getSuggestedWindowType(StartingWindowInfo windowInfo) { // For now we do not want to show any splash screens on TV. return STARTING_WINDOW_TYPE_NONE; } } |
以上为Andorid14 TvStaringWindowTypeAlgorithm。
在phone和tablet上,使用的是PhoneStartingWindowTypeAlgorithm实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgorithm { @Override public int getSuggestedWindowType(StartingWindowInfo windowInfo) { final int parameter = windowInfo.startingWindowTypeParameter; ... if (windowlessSurface) { return STARTING_WINDOW_TYPE_WINDOWLESS; } if (!topIsHome) { if (!processRunning || newTask || (taskSwitch && !activityCreated)) { return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen); } } if (taskSwitch) { if (allowTaskSnapshot) { if (windowInfo.taskSnapshot != null ) { return STARTING_WINDOW_TYPE_SNAPSHOT; } if (!topIsHome) { return STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN; } } if (!activityDrawn && !topIsHome) { return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen); } } return STARTING_WINDOW_TYPE_NONE; } private static int getSplashscreenType( boolean solidColorSplashScreen, boolean legacySplashScreen) { return solidColorSplashScreen ? STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN : legacySplashScreen ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN : STARTING_WINDOW_TYPE_SPLASH_SCREEN; } } |
我们可以让PhoneStartingWindowTypeAlgorithm直接返回STARTING_WINDOW_TYPE_NONE即可移除SPlash Screen。
本文为Adamin90原创文章,转载无需和我联系,但请注明来自http://www.lixiaopeng.top