一 点击桌面图标触发SplashScreen
1.1 点击桌面图标打开应用
点击桌面的短信图标,然后打开短信页面,使用winscope获取数据。
从点击短信图标到应用内容完全展开,中间有出现一个标题带有“Splash Screen”字符串的窗口。
二 Splash Screen窗口创建的源码调用流程
在aosp14源码中搜索Splash Screen关键字,可以发现SplashscreenContentDrawer.java这个类有创建这个标题的窗口对象。
//frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
public class SplashscreenContentDrawer {static WindowManager.LayoutParams createLayoutParameters(Context context,StartingWindowInfo windowInfo,@StartingWindowInfo.StartingWindowType int suggestType,CharSequence title, int pixelFormat, IBinder appToken) {final WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); ...代码省略... windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;params.flags = windowFlags;params.token = appToken;params.packageName = activityInfo.packageName;params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;//关键字params.setTitle("Splash Screen " + title);return params;}
}
在源码中查看调用,发现SplashscreenWindowCreator这个类的addSplashScreenStartingWindow方法有调用TaskSnapshotWindow的createLayoutParameters方法。
持续在源码中搜索查找,最终发现Splash Screen窗口的创建源码调用流程主要分为两个阶段,分别是SystemServer阶段和SystemUI阶段。
2.1 SystemUI阶段
由于SplashScreen窗口的创建是在SystemUI进程,所以我们先来看下SystemUI进程相关方法源码的调用流程。
//frameworks/base/core/java/android/window/TaskOrganizer.java
public class TaskOrganizer extends WindowOrganizer {private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() {@Overridepublic void addStartingWindow(StartingWindowInfo windowInfo) {//调用ShellTaskOrganizer的addStartingWindow方法mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(windowInfo));}}
}
//frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
public class ShellTaskOrganizer extends TaskOrganizer implementsCompatUIController.CompatUICallback { private StartingWindowController mStartingWindow;@Overridepublic void addStartingWindow(StartingWindowInfo info) {if (mStartingWindow != null) {//调用StartingWindowController的addStartingWindow方法mStartingWindow.addStartingWindow(info);}}
}
//frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
public class StartingWindowController implements RemoteCallable<StartingWindowController> {private final StartingSurfaceDrawer mStartingSurfaceDrawer;public void addStartingWindow(StartingWindowInfo windowInfo) {mSplashScreenExecutor.execute(() -> {Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addStartingWindow");final int suggestionType = mStartingWindowTypeAlgorithm.getSuggestedWindowType(windowInfo);final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo;if (suggestionType == STARTING_WINDOW_TYPE_WINDOWLESS) {mStartingSurfaceDrawer.addWindowlessStartingSurface(windowInfo);} else if (isSplashScreenType(suggestionType)) {//调用StartingSurfaceDrawer的addSplashScreenStartingWindow方法mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, suggestionType);} else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) {final TaskSnapshot snapshot = windowInfo.taskSnapshot;mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, snapshot);}...代码省略...Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);});}
}//frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
public class StartingSurfaceDrawer {private final SnapshotWindowCreator mSnapshotWindowCreator;void addSplashScreenStartingWindow(StartingWindowInfo windowInfo,@StartingWindowInfo.StartingWindowType int suggestType) {...代码省略...//调用SplashscreenContentDrawer的createLayoutParameters方法final WindowManager.LayoutParams params = SplashscreenContentDrawer.createLayoutParameters(context, windowInfo, suggestType, activityInfo.packageName,suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT, windowInfo.appToken);}
}
//frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
public class SplashscreenContentDrawer {static WindowManager.LayoutParams createLayoutParameters(Context context,StartingWindowInfo windowInfo,@StartingWindowInfo.StartingWindowType int suggestType,CharSequence title, int pixelFormat, IBinder appToken) {final WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); ...代码省略... windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;params.flags = windowFlags;params.token = appToken;params.packageName = activityInfo.packageName;params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;//关键字params.setTitle("Splash Screen " + title);return params;}
}
2.2 SystemUI阶段源码调用流程图
TaskOrganizer内部类ITaskOrganizer对象的addStartingWindow方法是被SystemServer跨进程调用的。
2.3 SystemServer阶段源码调用流程图
通过对systemServer进程断点调试,可以得到如下堆栈信息。
public class ActivityTaskManagerService extends IActivityTaskManager.Stub {private int startActivityAsUser(IApplicationThread caller, String callingPackage,@Nullable String callingFeatureId, Intent intent, String resolvedType,IBinder resultTo, String resultWho, int requestCode, int startFlags,ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {...代码省略...return getActivityStartController().obtainStarter(intent, "startActivityAsUser").setCaller(caller).setCallingPackage(callingPackage).setCallingFeatureId(callingFeatureId).setResolvedType(resolvedType).setResultTo(resultTo).setResultWho(resultWho).setRequestCode(requestCode).setStartFlags(startFlags).setProfilerInfo(profilerInfo).setActivityOptions(opts).setUserId(userId).execute();}
}
class ActivityStarter {int execute() {...代码省略...res = executeRequest(mRequest);...代码省略... }private int executeRequest(Request request) {...代码省略...mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,request.voiceInteractor, startFlags, checkedOptions,inTask, inTaskFragment, balVerdict, intentGrants, realCallingUid); ...代码省略... }private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,int startFlags, ActivityOptions options, Task inTask,TaskFragment inTaskFragment,BalVerdict balVerdict,NeededUriGrants intentGrants, int realCallingUid) {...代码省略... result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,startFlags, options, inTask, inTaskFragment, balVerdict,intentGrants, realCallingUid);...代码省略... }private Task mTargetRootTask;int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,int startFlags, ActivityOptions options, Task inTask,TaskFragment inTaskFragment, BalVerdict balVerdict,NeededUriGrants intentGrants, int realCallingUid) {...代码省略... mTargetRootTask.startActivityLocked(mStartActivity, topRootTask, newTask, isTaskSwitch, mOptions, sourceRecord); ...代码省略... }
}class Task extends TaskFragment {void startActivityLocked(ActivityRecord r, @Nullable Task topTask, boolean newTask,boolean isTaskSwitch, ActivityOptions options, @Nullable ActivityRecord sourceRecord) {...代码省略... if (r.mLaunchTaskBehind) {// Don't do a starting window for mLaunchTaskBehind. More importantly make sure we// tell WindowManager that r is visible even though it is at the back of the root// task.r.setVisibility(true);ensureActivitiesVisible(null /* starting */);// If launching behind, the app will start regardless of what's above it, so mark it// as unknown even before prior `pause`. This also prevents a race between set-ready// and activityPause. Launch-behind is basically only used for dream now.if (!r.isVisibleRequested()) {r.notifyUnknownVisibilityLaunchedForKeyguardTransition();}// Go ahead to execute app transition for this activity since the app transition// will not be triggered through the resume channel.mDisplayContent.executeAppTransition();} else if (SHOW_APP_STARTING_PREVIEW && doShow) {// Figure out if we are transitioning from another activity that is// "has the same starting icon" as the next one. This allows the// window manager to keep the previous window it had previously// created, if it still had one.Task baseTask = r.getTask();final ActivityRecord prev = baseTask.getActivity(a -> a.mStartingData != null && a.showToCurrentUser());//调用ActivityRecord的showStartingWindow方法mWmService.mStartingSurfaceController.showStartingWindow(r, prev, newTask,isTaskSwitch, sourceRecord);} }
}final class ActivityRecord extends WindowToken implements WindowManagerService.AppFreezeListener {void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,boolean processRunning, boolean startActivity, ActivityRecord sourceRecord,ActivityOptions candidateOptions) {...代码省略...final boolean scheduled = addStartingWindow(packageName, resolvedTheme,prev, newTask || newSingleActivity, taskSwitch, processRunning,allowTaskSnapshot(), activityCreated, mSplashScreenStyleSolidColor, allDrawn);...代码省略...} boolean addStartingWindow(String pkg, int resolvedTheme, ActivityRecord from, boolean newTask,boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot,boolean activityCreated, boolean isSimple,boolean activityAllDrawn) {...代码省略...ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Creating SplashScreenStartingData");mStartingData = new SplashScreenStartingData(mWmService, resolvedTheme, typeParameter);scheduleAddStartingWindow();return true;}private final AddStartingWindow mAddStartingWindow = new AddStartingWindow();void scheduleAddStartingWindow() {//调用AddStartingWindow的run方法mAddStartingWindow.run();}private class AddStartingWindow implements Runnable {@Overridepublic void run() {final StartingData startingData;...代码省略...surface = startingData.createStartingSurface(ActivityRecord.this);...代码省略... }
}
//frameworks/base/services/core/java/com/android/server/wm/SplashScreenStartingData.java
class SplashScreenStartingData extends StartingData {//父类StartingData的属性protected final WindowManagerService mService;@OverrideStartingSurface createStartingSurface(ActivityRecord activity) {//调用StartingSurfaceController的createSplashScreenStartingSurface方法return mService.mStartingSurfaceController.createSplashScreenStartingSurface(activity, mTheme);}
}//frameworks/base/services/core/java/com/android/server/wm/StartingSurfaceController.java
public class StartingSurfaceController {StartingSurface createSplashScreenStartingSurface(ActivityRecord activity, int theme) {synchronized (mService.mGlobalLock) {final Task task = activity.getTask();final TaskOrganizerController controller =mService.mAtmService.mTaskOrganizerController;//调用TaskOrganizerController的addStartingWindow方法if (task != null && controller.addStartingWindow(task, activity, theme,null /* taskSnapshot */)) {return new StartingSurface(task, controller.getTaskOrganizer());}}return null;}
}
//frameworks/base/services/core/java/com/android/server/wm/TaskOrganizerController.java
class TaskOrganizerController extends ITaskOrganizerController.Stub {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 = getTaskOrganizer();if (lastOrganizer == null) {return false;}final StartingWindowInfo info = task.getStartingWindowInfo(activity);if (launchTheme != 0) {info.splashScreenThemeResId = launchTheme;}info.taskSnapshot = taskSnapshot;info.appToken = activity.token;// make this happen prior than prepare surfacetry {//调用ITaskOrganizer的addStartingWindow方法 lastOrganizer.addStartingWindow(info);} catch (RemoteException e) {Slog.e(TAG, "Exception sending onTaskStart callback", e);return false;}return true;}
}