SplashScreen功能从android12开始添加,本文章分析了SplashScreen的启动流程,整体和独立App移除方案以及客制化
在开发者模式将Animation scale调成10x,adb shell输入命令:
dumpsys window windows
获取结果,以下为Splash Screen对应的Window信息
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行。
/** * 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脚本:
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错误。
接下来运行脚本:
./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]]
冷启动应用,终端输出调用栈信息:
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的匿名函数中执行的。
/** * 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调用栈:
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); }; } });
结果:
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方法。
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方法内调用。
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方法内:
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的调用栈:
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; }; } });
执行调用栈结果:
/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方法:
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:
/** * 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实例:
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