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
