一 點擊桌面圖標觸發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;}
}