Android13 SplashScreen功能源码分析、移除和客制化


SplashScreen功能从android12开始添加,本文章分析了SplashScreen的启动流程,整体和独立App移除方案以及客制化

获取SplashScreen的window名称

在开发者模式将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.

SplashScreen添加流程

通过上面代码可以看出在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.StartingWindowControlleraddStartingWindow的匿名函数中执行的。

/**
     * 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的添加流程,那如何移除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。


Adam博客
请先登录后发表评论
  • 最新评论
  • 总共0条评论
  • Powered by bjyblog modified by Adam © 2014-2024 www.lixiaopeng.com 版权所有 ICP证:鲁ICP备15039297号
  • 联系邮箱:14846869@qq.com