以下分析基于Android S. 
简述 前面几篇文章中,我们弄清楚了WMS中比较核心的几个类的作用以及初始化等流程。现在我们看看Activity启动时的启动窗口动画过程,以此为锲子剖析WMS相关流程。
启动窗口,如其名,最合理的地方应该是在Activity启动的时候播放其动画的,回到startActivityInner,开始看:
 
一. 启动窗口的初始化 1.1 ActivityStarter.startActivityInner 1 2 3 4 5 6 7 8 9 10 int  startActivityInner (final  ActivityRecord r, ActivityRecord sourceRecord,         IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,         int  startFlags, boolean  doResume, ActivityOptions options, Task inTask,         boolean  restrictedBgActivity, NeededUriGrants intentGrants)  {    ......          mTargetRootTask.startActivityLocked(mStartActivity,             topRootTask != null  ? topRootTask.getTopNonFinishingActivity() : null , newTask,             mKeepCurTransition, mOptions, startFromSamePackage);     ...... 
 
在Activity启动过程中,在创建新的Task并将该ActivityRecord保存其中,之后就是将启动Activity的流程转交给Task继续执行.
1.2 Task.startActivityLocked 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 void  startActivityLocked (ActivityRecord r, @Nullable  ActivityRecord focusedTopActivity,         boolean  newTask, boolean  keepCurTransition, ActivityOptions options,         boolean  samePackage)  {    ......     if  ((!isHomeOrRecentsRootTask() || hasActivity()) && allowMoveToFront) {         ......         boolean  doShow  =  true ;         ......                                    if  (r.mLaunchTaskBehind) {             ......         } else  if  (SHOW_APP_STARTING_PREVIEW && doShow) {                                       Task  prevTask  =  r.getTask();                          ActivityRecord  prev  =  prevTask.topActivityWithStartingWindow();             if  (prev != null ) {                                  if  (prev.getTask() != prevTask) {                     prev = null ;                 }                                  else  if  (prev.nowVisible) {                     prev = null ;                 }             }             final  int  splashScreenThemeResId  =  options != null                      ? options.getSplashScreenThemeResId() : 0 ;                          r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity),                     splashScreenThemeResId, samePackage);         }     ...... } private  boolean  isTaskSwitch (ActivityRecord r, ActivityRecord topFocusedActivity)  {    return  topFocusedActivity != null  && r.getTask() != topFocusedActivity.getTask(); } 
 
Task中执行Activity的启动主要是:
将该Activity对应的ActivityRecord放在该task合适的位置 
顺便检查是否需要显示启动窗口,如果需要:
检查是否可以重用启动窗口:
当前Activity和启动窗口不在同一个task时,不会重用启动窗口 
当前Activity已经显示出来了,也不会重用启动窗口 
 
 
显示启动窗口 
 
 
 
1.3 ActivityRecord.showStartingWindow 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 void  showStartingWindow (ActivityRecord prev, boolean  newTask, boolean  taskSwitch,         int  splashScreenTheme, boolean  samePackage)  {    if  (mTaskOverlay) {                  return ;     }     if  (mPendingOptions != null              && mPendingOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {                  return ;     }     final  CompatibilityInfo  compatInfo  =              mAtmService.compatibilityInfoForPackageLocked(info.applicationInfo);          final  int  resolvedTheme  =  evaluateStartingWindowTheme(packageName, theme,             splashScreenTheme);          final  boolean  shown  =  addStartingWindow(packageName, resolvedTheme,             compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,             prev != null  ? prev.appToken : null , newTask, taskSwitch, isProcessRunning(),             allowTaskSnapshot(),             mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal(),             samePackage);          if  (shown) {         mStartingWindowState = STARTING_WINDOW_SHOWN;     } } 
 
显示启动窗口首先需要检查是否需要显示,以下两种情况不显示启动窗口:
当此Activity为始终处于最上层时 
Activity动画类型为ANIM_SCENE_TRANSITION时 
 
然后选择合适窗口主题,添加启动窗口并在成功添加时,标记此ActivityRecord的启动窗口状态为STARTING_WINDOW_SHOWN,意为此Activity已经展示了启动窗口。
1.4 ActivityRecord.addStartingWindow 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 boolean  addStartingWindow (String pkg, int  resolvedTheme, CompatibilityInfo compatInfo,         CharSequence nonLocalizedLabel, int  labelRes, int  icon, int  logo, int  windowFlags,         IBinder transferFrom, boolean  newTask, boolean  taskSwitch, boolean  processRunning,         boolean  allowTaskSnapshot, boolean  activityCreated, boolean  samePackage)  {    ......          final  int  typeParameter  =  mWmService.mStartingSurfaceController             .makeStartingWindowTypeParameter(newTask, taskSwitch, processRunning,                     allowTaskSnapshot, activityCreated, samePackage);     ......          mStartingData = new  SplashScreenStartingData (mWmService, pkg,             resolvedTheme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,             getMergedOverrideConfiguration(), typeParameter);          scheduleAddStartingWindow();     return  true ; } void  scheduleAddStartingWindow ()  {              if  (StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {                  mAddStartingWindow.run();     } else  {                  if  (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {             ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Enqueueing ADD_STARTING" );             mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);         }     } } 
 
添加启动窗口的动作有很多,忽略细节,主要是封装该启动窗口的相关数据保存在SplashScreenStartingData中,然后执行启动窗口动画。
如果是转交给WMS的动画处理线程执行启动窗口的动画,而且是将该动画放在执行队列最前面执行。
至于WMS的mAnimationHandler是在类创建时初始化的:
1 2 3 4 5 6 7 final  Handler  mAnimationHandler  =  new  Handler (AnimationThread.getHandler().getLooper());private  AnimationThread ()  {    super ("android.anim" , THREAD_PRIORITY_DISPLAY, false  ); } 
 
所以mAnimationHandler执行所在的线程就是”android.anim”线程。
该ActivityRecord中的mAddStartingWindow也是类初始化时创建的:
1 2 3 4 5 6 private  final  AddStartingWindow  mAddStartingWindow  =  new  AddStartingWindow ();private  class  AddStartingWindow  implements  Runnable  {    ...... } 
 
二. 启动窗口的动画 2.1 AddStartingWindow.run 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 @Override public  void  run ()  {    final  StartingData startingData;     synchronized  (mWmService.mGlobalLock) {         ......         if  (mStartingData == null ) {                          ProtoLog.v(WM_DEBUG_STARTING_WINDOW,                     "startingData was nulled out before handling"                              + " mAddStartingWindow: %s" , ActivityRecord.this );             return ;         }         startingData = mStartingData;     }     ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Add starting %s: startingData=%s" ,             this , startingData);     WindowManagerPolicy.StartingSurface  surface  =  null ;     try  {                  surface = startingData.createStartingSurface(ActivityRecord.this );     } catch  (Exception e) {         Slog.w(TAG, "Exception when adding starting window" , e);     }     if  (surface != null ) {         boolean  abort  =  false ;         synchronized  (mWmService.mGlobalLock) {                          if  (mStartingData == null ) {                 ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Aborted starting %s: startingData=%s" ,                         ActivityRecord.this , mStartingData);                 mStartingWindow = null ;                 mStartingData = null ;                 abort = true ;             } else  {                                  mStartingSurface = surface;             }             ......         }         if  (abort) {                          surface.remove(false  );         }     ...... } 
 
AddStartingWindow就是一个runnable, 执行的时候先判断当前启动窗口是否仍需要,如果需要就创建启动窗口的Surface,如果启动窗口被中止,则移除创建的Surface.
2.2 StartingData.createStartingSurface ActivityRecord中的mStartingData就是SplashScreenStartingData类型的对象。SplashScreenStartingData是StartingData的子类。
1 2 3 4 5 6 7 @Override StartingSurface createStartingSurface (ActivityRecord activity)  {     return  mService.mStartingSurfaceController.createSplashScreenStartingSurface(             activity, mPkg, mTheme, mCompatInfo, mNonLocalizedLabel, mLabelRes, mIcon,             mLogo, mWindowFlags, mMergedOverrideConfiguration,             activity.getDisplayContent().getDisplayId()); } 
 
通过WMS中的mStartingSurfaceController来创建StartingSurface, mStartingSurfaceController是在WMS初始化的时候创建,用来管理创建和释放启动窗口Surface。
2.3 StartingSurfaceController.createSplashScreenStartingSurface 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 StartingSurface createSplashScreenStartingSurface (ActivityRecord activity, String packageName,          int  theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int  labelRes,         int  icon, int  logo, int  windowFlags, Configuration overrideConfig, int  displayId)  {    ......     synchronized  (mService.mGlobalLock) {                  final  Task  task  =  activity.getTask();                           if  (task != null  && mService.mAtmService.mTaskOrganizerController.addStartingWindow(                 task, activity.token, theme, null  )) {                          return  new  ShellStartingSurface (task);         }     }     return  null ; } 
 
最终创建的Surface是ShellStartingSurface.
2.4 TaskOrganizerController.addStartingWindow 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 boolean  addStartingWindow (Task task, IBinder appToken, int  launchTheme,         TaskSnapshot taskSnapshot)  {    final  Task  rootTask  =  task.getRootTask();     if  (rootTask == null  || rootTask.mTaskOrganizer == null ) {         return  false ;     }     final  TaskOrganizerState  state  =              mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());          state.addStartingWindow(task, appToken, launchTheme, taskSnapshot);     return  true ; } private  final  TaskOrganizerCallbacks mOrganizer;void  addStartingWindow (Task t, IBinder appToken, int  launchTheme,         TaskSnapshot taskSnapshot)  {    mOrganizer.addStartingWindow(t, appToken, launchTheme, taskSnapshot); } final  ITaskOrganizer mTaskOrganizer;void  addStartingWindow (Task task, IBinder appToken, int  launchTheme,         TaskSnapshot taskSnapshot)  {    final  StartingWindowInfo  info  =  task.getStartingWindowInfo();     if  (launchTheme != 0 ) {         info.splashScreenThemeResId = launchTheme;     }     info.mTaskSnapshot = taskSnapshot;     try  {                  mTaskOrganizer.addStartingWindow(info, appToken);     } catch  (RemoteException e) {         Slog.e(TAG, "Exception sending onTaskStart callback" , e);     } } 
 
这里有点绕,我们看看mTaskOrganizer是什么,看看TaskOrganizerCallbacks和TaskOrganizerState的初始化可以知道:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 TaskOrganizerCallbacks(ITaskOrganizer taskOrg,         Consumer<Runnable> deferTaskOrgCallbacksConsumer) {     mDeferTaskOrgCallbacksConsumer = deferTaskOrgCallbacksConsumer;          mTaskOrganizer = taskOrg; } TaskOrganizerState(ITaskOrganizer organizer, int  uid) {     final  Consumer<Runnable> deferTaskOrgCallbacksConsumer =             mDeferTaskOrgCallbacksConsumer != null                      ? mDeferTaskOrgCallbacksConsumer                     : mService.mWindowManager.mAnimator::addAfterPrepareSurfacesRunnable;     mOrganizer = new  TaskOrganizerCallbacks (organizer, deferTaskOrgCallbacksConsumer);     mDeathRecipient = new  DeathRecipient (organizer);     try  {         organizer.asBinder().linkToDeath(mDeathRecipient, 0 );     } catch  (RemoteException e) {         Slog.e(TAG, "TaskOrganizer failed to register death recipient" );     }     mUid = uid; } 
 
所以mTaskOrganizer还是TaskOrganizerState初始化时传入的。那么TaskOrganizerState是怎么初始化的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override public  ParceledListSlice<TaskAppearedInfo> registerTaskOrganizer (ITaskOrganizer organizer)  {    ......     try  {         synchronized  (mGlobalLock) {             ......             if  (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {                 mTaskOrganizers.add(organizer);                 mTaskOrganizerStates.put(organizer.asBinder(),                         new  TaskOrganizerState (organizer, uid));             }             .......         ......     ...... } 
 
所以是有其他进程调用了TaskOrganizerController的registerTaskOrganizer将该成员注册了,而这里的方法仅有TaskOrganizer.java#72调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @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));     }     ...... } @BinderThread public  void  addStartingWindow (@NonNull  StartingWindowInfo info,         @NonNull  IBinder appToken)  {}
 
所以这个是将启动窗口的信息通过binder调用,添加到实现TaskOrganizer的addStartingWindow方法的其他进程了,并非是系统进程管控。
总的来说AddStartingWindow.run这个就是做了两件事:
创建启动窗口Surface, 即ShellStartingSurface 
将启动窗口信息通过binder调用传递给实现TaskOrganizer的进程 
 
这里的流程和R的差异有点大,推测是Google希望启动窗口可以交给Launcher或者SystemUI实现,而不是由framework的system_server进程来管控。
搜索SystemUI源码可以发现,继承TaskOrganizer的类有一个:ShellTaskOrganizer!
2.5 ShellTaskOrganizer.addStartingWindow 1 2 3 4 5 6 @Override public  void  addStartingWindow (StartingWindowInfo info, IBinder appToken)  {    if  (mStartingWindow != null ) {         mStartingWindow.addStartingWindow(info, appToken);     } } 
 
看来Google还真是将启动窗口交给SystemUI模块了。
三. SystemUI负责的启动窗口 上面我们知道一个Activity启动后,从App进程交给SystemServer处理,在创建该Activity的ActivityRecord, 并将其保存在对应的Task之后,会在需要的时候开始启动窗口的播放。
启动窗口在Android S上是交给SystemUI进程负责了,在以前是交给SystemServer的“android.anim”线程处理。
关于R上SystemUI的ShellTaskOrganizer这一块涉及到Dagger2, 是一款基于 Java 注解来实现的在编译阶段完成依赖注入的开源库 的使用,这里先不展开讲了,感兴趣的可以自行研究。
ShellTaskOrganizer中的mStartingWindow是StartingWindowController类对象。
3.1 StartingWindowController.addStartingWindow 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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  (mTaskLaunchingCallback != null  && shouldSendToListener(suggestionType)) {             mTaskLaunchingCallback.accept(runningTaskInfo.taskId, suggestionType);         }         if  (suggestionType == STARTING_WINDOW_TYPE_SPLASH_SCREEN) {             mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken,                     false  );         } else  if  (suggestionType == STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN) {             mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken,                     true  );         } else  if  (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) {             final  TaskSnapshot  snapshot  =  windowInfo.mTaskSnapshot;             mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken,                     snapshot);         } else   {                      }         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);     }); } 
 
根据启动窗口类型选择不同的启动窗口,这里我们假设是STARTING_WINDOW_TYPE_SPLASH_SCREEN类型。
3.2 StartingSurfaceDrawer.addSplashScreenStartingWindow 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 void  addSplashScreenStartingWindow (StartingWindowInfo windowInfo, IBinder appToken,         boolean  emptyView)  {    final  RunningTaskInfo  taskInfo  =  windowInfo.taskInfo;     final  ActivityInfo  activityInfo  =  taskInfo.topActivityInfo;     ......     Context  context  =  mContext;     ......          final  int [] splashscreenContentResId = new  int [1 ];          getWindowResFromContext(context, a -> {         splashscreenContentResId[0 ] =                 a.getResourceId(R.styleable.Window_windowSplashscreenContent, 0 );         showWallpaper[0 ] = a.getBoolean(R.styleable.Window_windowShowWallpaper, false );     });     ......     final  PhoneWindow  win  =  new  PhoneWindow (context);     win.setIsStartingWindow(true );     ......     win.setType(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);     ......     win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,             WindowManager.LayoutParams.MATCH_PARENT);     final  WindowManager.LayoutParams  params  =  win.getAttributes();          params.token = appToken;     params.packageName = activityInfo.packageName;     ......     params.setTitle("Splash Screen "  + activityInfo.packageName);     final  SplashScreenViewSupplier  viewSupplier  =  new  SplashScreenViewSupplier ();     final  Runnable  setViewSynchronized  =  () -> {                           SplashScreenView  contentView  =  viewSupplier.get();         ......                  win.setContentView(contentView);         contentView.cacheRootWindow(win);         ......     }          mSplashscreenContentDrawer.createContentView(context, emptyView,             splashscreenContentResId[0 ], activityInfo, taskId, viewSupplier::setView);     ......     final  View  view  =  win.getDecorView();     final  WindowManager  wm  =  mContext.getSystemService(WindowManager.class);          postAddWindow(taskId, appToken, view, wm, params);          mChoreographer.postCallback(CALLBACK_INSETS_ANIMATION, setViewSynchronized, null ); } protected  void  postAddWindow (int  taskId, IBinder appToken, View view, WindowManager wm,         WindowManager.LayoutParams params)  {    ......               wm.addView(view, params);     ...... } 
 
这里创建启动窗口对应的PhoneWindow以及设置相关的参数,并将启动窗口的DecorView和窗口属性通过binder传给WMS用来创建对应的WindowState,最后通过Choreographer请求vsync,在下一帧的时候设置该启动窗口的内容。
3.3 SplashscreenContentDrawer.createContentView 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 void  createContentView (Context context, boolean  emptyView, int  splashScreenResId,         ActivityInfo info, int  taskId, Consumer<SplashScreenView> consumer)  {    mSplashscreenWorkerHandler.post(() -> {         SplashScreenView contentView;         try  {                          contentView = SplashscreenContentDrawer.makeSplashscreenContent(                     context, splashScreenResId);                          if  (contentView == null ) {                 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "makeSplashScreenContentView" );                                  if  (emptyView) {                     contentView = makeEmptySplashScreenContentView(context);                 } else  {                                          contentView = makeSplashScreenContentView(context, info);                 }                 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);             }         } catch  (RuntimeException e) {             Slog.w(TAG, "failed creating starting window content at taskId: "                      + taskId, e);             contentView = null ;         }                  consumer.accept(contentView);     }); } 
 
这里是创建启动窗口显示的内容,首先通过SplashscreenContentDrawer创建SplashScreenView.
3.3.1 SplashscreenContentDrawer.makeSplashscreenContent 1 2 3 4 5 6 7 8 9 10 private  static  SplashScreenView makeSplashscreenContent (Context ctx,         int  splashscreenContentResId)  {         final  int  targetSdkVersion  =  ctx.getApplicationInfo().targetSdkVersion;          if  (targetSdkVersion >= Build.VERSION_CODES.S) {         return  null ;     }     ...... } 
 
Andriod S之后不支持Window_windowSplashscreenContent了.
3.3.2 SplashscreenContentDrawer.makeSplashScreenContentView 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 private  SplashScreenView makeSplashScreenContentView (Context context, ActivityInfo ai)  {         updateDensity();          getWindowAttrs(context, mTmpAttrs);     final  StartingWindowViewBuilder  builder  =  new  StartingWindowViewBuilder ();     final  int  animationDuration;     Drawable iconDrawable;          if  (mTmpAttrs.mReplaceIcon != null ) {         iconDrawable = mTmpAttrs.mReplaceIcon;         animationDuration = Math.max(0 ,                 Math.min(mTmpAttrs.mAnimationDuration, mMaxAnimatableIconDuration));     } else  {         iconDrawable = mIconProvider.getIconForUI(                 ai, getUserHandleForUid(ai.applicationInfo.uid));         if  (iconDrawable == null ) {             iconDrawable = context.getPackageManager().getDefaultActivityIcon();         }         animationDuration = 0 ;     }          final  int  themeBGColor  =  peekWindowBGColor(context);          return  builder             .setContext(context)             .setWindowBGColor(themeBGColor)             .setIconDrawable(iconDrawable)             .setIconAnimationDuration(animationDuration)             .setBrandingDrawable(mTmpAttrs.mBrandingImage)             .setIconBackground(mTmpAttrs.mIconBgColor).build(); } 
 
这里首先重置相关配置属性,然后根据默认配置创建SplashScreenView。
3.3.3 StartingWindowViewBuilder.build 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 SplashScreenView build ()  {     ......          if  (!processAdaptiveIcon() && mIconDrawable != null ) {         if  (DEBUG) {             Slog.d(TAG, "The icon is not an AdaptiveIconDrawable" );         }         createIconDrawable(mIconDrawable, mIconSize);     }     final  int  iconSize  =  mFinalIconDrawable != null  ? (int ) (mIconSize * mScale) : 0 ;          mCachedResult = fillViewWithIcon(mContext, iconSize, mFinalIconDrawable);     mBuildComplete = true ;     return  mCachedResult; } 
 
这里也仅仅是检查了下mIconDrawable是否为AdaptiveIconDrawable的实例,如果不是则重新创建一个。然后使用fillViewWithIcon填充内容,创建SplashScreenView.
3.3.4 StartingWindowViewBuilder.fillViewWithIcon 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private  SplashScreenView fillViewWithIcon (Context context,         int  iconSize, Drawable iconDrawable)  {    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "fillViewWithIcon" );     final  SplashScreenView.Builder  builder  =  new  SplashScreenView .Builder(context);     builder.setIconSize(iconSize).setBackgroundColor(mThemeColor)             .setIconBackground(mIconBackground);     if  (iconDrawable != null ) {         builder.setCenterViewDrawable(iconDrawable);     }     builder.setAnimationDurationMillis(mIconAnimationDuration);     if  (mBrandingDrawable != null ) {         builder.setBrandingDrawable(mBrandingDrawable, mBrandingImageWidth,                 mBrandingImageHeight);     }          final  SplashScreenView  splashScreenView  =  builder.build();     if  (DEBUG) {         Slog.d(TAG, "fillViewWithIcon surfaceWindowView "  + splashScreenView);     }     splashScreenView.makeSystemUIColorsTransparent();     Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);     return  splashScreenView; } 
 
设置待创建的SplashScreenView相关的内容,如主题颜色、图标背景色、图标动画时长、三方应用的图标等等,然后通过build构建SplashScreenView.
3.3.5 SplashScreenView.Builder.build 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public  SplashScreenView build ()  {    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "SplashScreenView#build" );     final  LayoutInflater  layoutInflater  =  LayoutInflater.from(mContext);     final  SplashScreenView  view  =  (SplashScreenView)             layoutInflater.inflate(R.layout.splash_screen_view, null , false );     ......     view.mIconView = view.findViewById(R.id.splashscreen_icon_view);     view.mBrandingImageView = view.findViewById(R.id.splashscreen_branding_view);     ......          if  (mIconDrawable != null ) {         view.mIconView.setBackground(mIconDrawable);         view.initIconAnimation(mIconDrawable,                 mIconAnimationDuration != null  ? mIconAnimationDuration.toMillis() : 0 );     }     ......          if  (mBrandingImageHeight > 0  && mBrandingImageWidth > 0  && mBrandingDrawable != null ) {         ......         view.mBrandingImageView.setBackground(mBrandingDrawable);     }     ......     Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);     return  view; } 
 
SplashScreenView是继承FrameLayout的,通过LayoutInflater实例化splash_screen_view作为其内容布局:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <android.window.SplashScreenView      xmlns:android ="http://schemas.android.com/apk/res/android"      android:layout_height ="match_parent"      android:layout_width ="match_parent"      android:orientation ="vertical" >     <View  android:id ="@+id/splashscreen_icon_view"             android:layout_height ="wrap_content"            android:layout_width ="wrap_content"            android:layout_gravity ="center"            android:contentDescription ="@string/splash_screen_view_icon_description" />     <View  android:id ="@+id/splashscreen_branding_view"             android:layout_height ="wrap_content"            android:layout_width ="wrap_content"            android:layout_gravity ="center_horizontal|bottom"            android:layout_marginBottom ="60dp"            android:contentDescription ="@string/splash_screen_view_branding_description" /> </android.window.SplashScreenView > 
 
这个非常简单,就是两个View, 所以说启动窗口的显示内容就是两个图标(如果有图标对应的image不为null,则会显示)。
3.4 SplashScreenViewSupplier.setView 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 private  static  class  SplashScreenViewSupplier  implements  Supplier <SplashScreenView> {    private  SplashScreenView mView;     private  boolean  mIsViewSet;     void  setView (SplashScreenView view)  {         synchronized  (this ) {                          mView = view;             mIsViewSet = true ;             notify();         }     }     @Override      public  @Nullable  SplashScreenView get ()  {         synchronized  (this ) {             while  (!mIsViewSet) {                 try  {                     wait();                 } catch  (InterruptedException ignored) {                 }             }             return  mView;         }     } } 
 
这个类的主要作用就是将SplashScreenView的创建交给与使用线程不同的线程。
四.小结 来一张流程图回顾下启动窗口被添加的过程:
虽然启动窗口对应的View和内容是通过SystemUI创建的,但是其还是被WMS归属为待启动的Activity的Task中的一员。究其原因还是其窗口属性中的token是该Activity对应的ActivityRecord.Token。SystemUI创建启动窗口的内容后,会在下一帧来时将该View添加到WMS中,然后WMS就会在addWindow里创建对应的WindowState。