--- title: 'WindowManagerService' disqus: kyleAlien --- WindowManagerService === ## OverView of Content [TOC] ## WMS 概述 WMS 全名是 WindowManagerService,它是客戶端 APP 的管理者,處於 SystemServer 進程,WMS 主要負責管理幾件事情 1. **窗口管理** 負責 Window 添加、更新、刪除,核心管理成員有 **`DisplayContent`、`WindowToken`、`WindowState`** 2. **窗口動畫** Window 切換時,可以添加動畫,讓 Window 切換時更加自然,動畫由 WMS 的動畫子系統負責,該系統是 **`WindowAnimator`** 3. **輸入系統的中轉** 當使用者對視窗觸控 or 輸入時,都會觸發 InputManagerService 會對觸摸事件進行處理,將事件通過 WMS 傳遞到合適的窗口進行處理 (這時 WMS 就是 **中轉站**) 4. **Surface 管理** WMS 並不具備 Window 繪製的功能,**每個 Window 都有一個 Surface 來繪製**,而 WMS 負責 Surface 的分配 ## WMS 啟動 - [SystemServer](https://android.googlesource.com/platform/frameworks/base/+/master/services/java/com/android/server/SystemServer.java) * WMS 是就是 WindowManagerService,它是系統服務,說到系統服務自然就會想到 SystemServer,那就讓我們來看看 WMS 是如何被 SystemServer 喚起的 1. SystemServer 透過 WMS#main 方法啟動 2. 把 WMS 以 Context#WINDOW_SERVICE (**window**) 設定到 ServiceManager 中 3. 把啟動的 WMS 對象設定給 AMS 服務 ```java= // SystemServer.java private void run() { ... 省略部分 // Start services. try { t.traceBegin("StartServices"); startBootstrapServices(t); startCoreServices(t); // @ WMS 是在 OtherServices 啟動 startOtherServices(t); } catch (Throwable ex) { ... 省略 Log throw ex; } /* 省略 finally */ } private void startOtherServices(@NonNull TimingsTraceAndSlog t) { ... 省略部分 try { t.traceBegin("StartWindowManagerService"); // WMS needs sensor service ready mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE); // 1. 啟動 WMS wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore, new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager); // 2. 對 ServiceManager 添加服務 "window" ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO); ServiceManager.addService(Context.INPUT_SERVICE, inputManager, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL); t.traceEnd(); // 3. 與 AMS 產生關係 t.traceBegin("SetWindowManagerService"); mActivityManagerService.setWindowManager(wm); t.traceEnd(); } /* 省略 catch */ } ``` * 在 WMS 啟動後會透過 `onInitReady`、`displayReady`、`systemReady` 來完成 WMS 的後續動作 (這些方法之後會介紹) ```java= // SystemServer.java private void startOtherServices(@NonNull TimingsTraceAndSlog t) { ... 省略部分 try { // 初始化準備 wm.onInitReady(); ... 省略部分 try { wm.displayReady(); } catch (Throwable e) { reportWtf("making display ready", e); } wm.systemReady(); } /* 省略 catch */ } ``` ### [WMS](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/WindowManagerService.java) - main 函數 * 透過 DisplayThread 中的 Handler#runWithScissors 創建 WMS 對象 (也就是創建 WMS 的線程與 SystemServer 線程不同) * Handler#**runWithScissors** 方法是透過鎖來達成同步任務,保證該 Runnable 會先被 DisplayThread 的 Looper 先執行,執行完後才回到 SystemServer 的 Thread > 透過 synchronized、wait 鎖住 SystemServer Thread > > ![](https://i.imgur.com/J055pGW.png) ```java= // WindowManagerService.java public static WindowManagerService main(final Context context, final InputManagerService im, final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy, ActivityTaskManagerService atm) { // @ 查看 main 方法 return main(context, im, showBootMsgs, onlyCore, policy, atm, new DisplayWindowSettingsProvider(), SurfaceControl.Transaction::new, Surface::new, SurfaceControl.Builder::new); } @VisibleForTesting public static WindowManagerService main(final Context context, final InputManagerService im, final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy, ActivityTaskManagerService atm, ... 省略部分參數) { // 創建新 Thread DisplayThread.getHandler().runWithScissors(() -> sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy, atm, displayWindowSettingsProvider, transactionFactory, surfaceFactory, surfaceControlFactory), 0); // 執行完 runWithScissors 中的 Runnable 任務後才會返回 return sInstance; } ``` ## [HandlerThread](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/HandlerThread.java) - 創建 Looper 這裡特別介紹了 HandlerThread 這個類,^1^ 它繼承於 Thread 並且會在 run 方法中自動 ,**^2^ 建立該 Thread 對象專屬的 Looper 對象,但並 ++不主動創建 Handler 對象++ (在這創建的 Handler 並不安全)** ```java= // HandlerThread.java public class HandlerThread extends Thread { ... 省略部分方法 @Override public void run() { mTid = Process.myTid(); // 準備該 Thread 專用的 Looper & MessageQueue 對象 Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); // 通知所有等待的 Thread, Looper 已經創建完成 notifyAll(); } Process.setThreadPriority(mPriority); // Looper 準備好 onLooperPrepared(); // 準備開始運行 Loop 不斷循環處理 Message 任務 Looper.loop(); mTid = -1; } public Looper getLooper() { if (!isAlive()) { return null; } // If the thread has been started, wait until the looper has been created. synchronized (this) { // 以當前對象作為鎖 while (isAlive() && mLooper == null) { try { wait(); // 直到 Looper 創建 } catch (InterruptedException e) { } } } return mLooper; } // 在需要時才創建 (但線程不安全) public Handler getThreadHandler() { if (mHandler == null) { mHandler = new Handler(getLooper()); } return mHandler; } } ``` > ![](https://i.imgur.com/g1GxcvF.png) ### [DisplayThread](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/DisplayThread.java) - 創建 WMS 對象 :::success WMS 就是在 DisplayThread 類中被啟動,**DisplayThread 可以說是 WMS 的 MainThread** ::: * DisplayThread 可以創建安全 Handler,其主要功能是:處理 **低延遲顯示** 的相關操作,並只能由 WindowManager、DisplayManager、InputManager 執行快速操作 * 從上面可以看到 DisplayThread 繼承於 ServiceThread (而它繼承於 HandlerThread),所以 **它會在執行 run 方法時自動創建 Looper 對象**,鑒於 HandlerThread 創建的 Handler 並不安全,所以 DisplayThread 這裡自己創建 Handler (使用類鎖) ```java= // DisplayThread.java public final class DisplayThread extends ServiceThread { private static DisplayThread sInstance; private static Handler sHandler; // 單例模式 private DisplayThread() { // DisplayThread runs important stuff, but these are not as important as things running in // AnimationThread. Thus, set the priority to one lower. super("android.display", Process.THREAD_PRIORITY_DISPLAY + 1, false /*allowIo*/); } private static void ensureThreadLocked() { if (sInstance == null) { sInstance = new DisplayThread(); sInstance.start(); sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER); sHandler = new Handler(sInstance.getLooper()); } } public static DisplayThread get() { // 類鎖 synchronized (DisplayThread.class) { // 創建 DisplayThread 對象 ensureThreadLocked(); return sInstance; } } // 透過 DisplayThread.class 類鎖來達成安全創建 public static Handler getHandler() { // 類鎖 synchronized (DisplayThread.class) { // 創建 DisplayThread 對象 ensureThreadLocked(); return sHandler; } } } ``` :::success * 在取得 DisplayThread 類時,該類的 Thread 就已經啟動 (透過#ensureThreadLocked 方法啟動 Thread),這時再對該類放置任務就會開始執行 * **Binder Thread 傳遞的訊息可以透過 DisplayThread 傳遞給 WMS** ::: * 傳遞事件給 WMS 有兩種方式 1. 直接呼叫 WMS function,即時傳送 ```java= // WindowManagerService.java @Override public boolean isKeyguardLocked() { // mPolicy 類型 WindowManagerPolicy return mPolicy.isKeyguardLocked(); } ``` 2. 透過 DisplayThread 的 Looper 將事件丟入 WMS Thread (DisplayThread) 中 ```java= // WindowManagerService.java final H mH = new H(); final class H extends android.os.Handler { ... } @Override public void setAnimationScale(int which, float scale) { if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE, "setAnimationScale()")) { throw new SecurityException("Requires SET_ANIMATION_SCALE permission"); } scale = fixScale(scale); switch (which) { case 0: mWindowAnimationScaleSetting = scale; break; case 1: mTransitionAnimationScaleSetting = scale; break; case 2: mAnimatorDurationScaleSetting = scale; break; } // Persist setting mH.sendEmptyMessage(H.PERSIST_ANIMATION_SCALE); } ``` > ![](https://i.imgur.com/kSRGq94.png) ### [UiThread](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/services/core/java/com/android/server/UiThread.java) 類 * UiThread 與 DisplayThread 差不多,但多添加了一個 `dispose` 方法,來停止 Looper,並結束該對象 (置為 null),在 `initPolicy()`、`systemReady()` 會使用到 ```java= // UiThread.java public final class UiThread extends ServiceThread { private static final long SLOW_DISPATCH_THRESHOLD_MS = 100; private static final long SLOW_DELIVERY_THRESHOLD_MS = 200; private static UiThread sInstance; private static Handler sHandler; private UiThread() { super("android.ui", Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/); } @Override public void run() { // Make sure UiThread is in the fg stune boost group Process.setThreadGroup(Process.myTid(), Process.THREAD_GROUP_TOP_APP); super.run(); } private static void ensureThreadLocked() { if (sInstance == null) { sInstance = new UiThread(); sInstance.start(); final Looper looper = sInstance.getLooper(); looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER); looper.setSlowLogThresholdMs( SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS); sHandler = new Handler(sInstance.getLooper()); } } public static UiThread get() { synchronized (UiThread.class) { ensureThreadLocked(); return sInstance; } } public static Handler getHandler() { synchronized (UiThread.class) { ensureThreadLocked(); return sHandler; } } /** * Disposes current ui thread if it's initialized. Should only be used in tests to set up a * new environment. */ @VisibleForTesting public static void dispose() { synchronized (UiThread.class) { if (sInstance == null) { return; } // 執行完 runWithScissors 才會往下執行 getHandler().runWithScissors(sInstance::quit, 0 /* timeout */); sInstance = null; } } } ``` ### [AnimationThread](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/AnimationThread.java) 類 * AnimationThread 與 DisplayThread 差不多,但多添加了一個 dispose 方法,來停止 Looper,並結束該對象 (置為 null) ```java= // AnimationThread.java public final class AnimationThread extends ServiceThread { private static AnimationThread sInstance; private static Handler sHandler; private AnimationThread() { super("android.anim", THREAD_PRIORITY_DISPLAY, false /*allowIo*/); } private static void ensureThreadLocked() { if (sInstance == null) { sInstance = new AnimationThread(); sInstance.start(); sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER); sHandler = new Handler(sInstance.getLooper()); } } public static AnimationThread get() { synchronized (AnimationThread.class) { ensureThreadLocked(); return sInstance; } } public static Handler getHandler() { synchronized (AnimationThread.class) { ensureThreadLocked(); return sHandler; } } // 使用到 HandlerThread#quit 方法,停止 Looper @VisibleForTesting public static void dispose() { synchronized (AnimationThread.class) { if (sInstance == null) { return; } getHandler().runWithScissors(() -> sInstance.quit(), 0 /* timeout */); sInstance = null; } } } ``` ## SystemServer - WMS 啟動後續 複習一下:在 SystemService 啟動 WMS 後會隨之調用到 WMS#**`onInitReady` 方法** -> `displayReady` 方法 -> `systemReady` 方法 ```java= // SystemServer.java private void startOtherServices(@NonNull TimingsTraceAndSlog t) { ... 省略部分 try { // 初始化準備 wm.onInitReady(); ... 省略部分 try { wm.displayReady(); } catch (Throwable e) { reportWtf("making display ready", e); } wm.systemReady(); } /* 省略 catch */ } ``` ### [WMS](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/WindowManagerService.java#1209) 創建 * 前面有提到 WindowManagerService#`main` 方法會透過 DisplayThread (Thread) 創建 WMS 的實例,在這裡就會創建 **H 類**,而它的 **Looper 就是當前 Thread 也就是 DisplayThread** ```java= // WindowManagerService.java // 當沒有指定 Looper 就會依現在 Thread 的 Looper final H mH = new H(); // 也就是 DisplayThread 的 Looper WindowManagerPolicy mPolicy; final WindowAnimator mAnimator; RootWindowContainer mRoot; final class H extends android.os.Handler { ... } private WindowManagerService(Context context, InputManagerService inputManager, boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy, ActivityTaskManagerService atm, DisplayWindowSettingsProvider displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory, Supplier<Surface> surfaceFactory, Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) { ... 省略部分 // WMS 持有 InputManagerService 引用 mInputManager = inputManager; ... // 取得 DisplayManagerService 代理 mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); mPolicy = policy; // ++ SystemServer 傳進來的實例是 PhoneWindowManager 類 ++ // 取得 WMS 代理 mActivityManager = ActivityManager.getService(); mActivityTaskManager = ActivityTaskManager.getService(); // Window 動畫 mAnimator = new WindowAnimator(this); // 所有 Window 的根節點 (RootWindowContainer 對象) mRoot = new RootWindowContainer(this); } ``` ### [WMS](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/WindowManagerService.java)#onInitReady - 初始化 WindowManagerPolicy * WMS#**onInitReady** 函數:**取得 UiThread (另外一個 Thread) 的 Handler,並 ==初始化 [WindowManagerPolicy](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java)== (實作呼叫 PhoneWindowManager#init 函數)** ```java= // WindowManagerService.java public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs { WindowManagerPolicy mPolicy; // 實作是 ++ PhoneWindowManager 類 ++ public void onInitReady() { // @ 重點分析 initPolicy 函數 initPolicy(); ... 省略部分 } private void initPolicy() { UiThread.getHandler().runWithScissors(new Runnable() { @Override public void run() { WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper()); mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this); } }, 0); } } // --------------------------------------------- // WindowManagerPolicy.java public interface WindowManagerPolicy extends WindowManagerPolicyConstants { ... 省略部分 public void init(Context context, IWindowManager windowManager, WindowManagerFuncs windowManagerFuncs); // ---------------------------------------------- // PhoneWindowManager.java public class PhoneWindowManager implements WindowManagerPolicy { ... 省略部分 private class PolicyHandler extends Handler { ... 省略部分 } public void init(Context context, IWindowManager windowManager, WindowManagerFuncs windowManagerFuncs) { // 省略部分 ... 後面介紹 mHandler = new PolicyHandler(); // 該 Handler 使用 UiThread's Looper } } ``` :::info * 從這裡可以知道,`WindowManagerPolicy` & `WMS` & `SystemServer` 都在不同 Thread 中運作 ::: ### [WMS](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/WindowManagerService.java)#displayReady - 準備顯示 1. 標示 WindowAnimator 初始化完成 2. 重新配置設定,保證 DisplayWindowSettings 設定都有被正常使用 3. 透過 ActivityTaskManager 來更新設置 ```java= // WindowManagerService.java WindowManagerPolicy mPolicy; final WindowAnimator mAnimator; // 在 WMS construct 被初始化 RootWindowContainer mRoot; public void displayReady() { synchronized (mGlobalLock) { // ActivityTaskManagerService 中的對象 if (mMaxUiWidth > 0) { mRoot.forAllDisplays(displayContent -> displayContent.setMaxUiWidth(mMaxUiWidth)); } applyForcedPropertiesForDefaultDisplay(); // 1. 標示初始化完成 mAnimator.ready(); mDisplayReady = true; // Reconfigure all displays to make sure that forced properties and // DisplayWindowSettings are applied. // 2. 重新配置設定,保證 DisplayWindowSettings 設定都有被正常使用 mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked); // 硬體是否是可觸控螢幕 mIsTouchDevice = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_TOUCHSCREEN); // 硬體是否是 假的可觸控螢幕 mIsFakeTouchDevice = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_FAKETOUCH); } try { // 3. 透過 ActivityTaskManager 來更新設置 mActivityTaskManager.updateConfiguration(null); } catch (RemoteException e) { } } ``` ### [WMS](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/WindowManagerService.java)#systemReady - 服務準備完成 * WMS 服務準備完成 ```java= // WindowManagerService.java public void systemReady() { mSystemReady = true; mPolicy.systemReady(); mRoot.forAllDisplayPolicies(DisplayPolicy::systemReady); mTaskSnapshotController.systemReady(); mHasWideColorGamutSupport = queryWideColorGamutSupport(); mHasHdrSupport = queryHdrSupport(); mPrimaryDisplayOrientation = queryPrimaryDisplayOrientation(); mPrimaryDisplayPhysicalAddress = DisplayAddress.fromPhysicalDisplayId(SurfaceControl.getPrimaryPhysicalDisplayId()); UiThread.getHandler().post(mSettingsObserver::loadSettings); IVrManager vrManager = IVrManager.Stub.asInterface( ServiceManager.getService(Context.VR_SERVICE)); if (vrManager != null) { try { final boolean vrModeEnabled = vrManager.getVrModeState(); synchronized (mGlobalLock) { vrManager.registerListener(mVrStateCallbacks); if (vrModeEnabled) { mVrModeEnabled = vrModeEnabled; mVrStateCallbacks.onVrStateChanged(vrModeEnabled); } } } catch (RemoteException e) { // Ignore, we cannot do anything if we failed to register VR mode listener } } } ``` ### WMS 啟動流程圖 > ![](https://i.imgur.com/5oB1XUK.png) ## WMS 重點 ### WMS IPC - Binder 通訊 * 這裡涉及三個對象 `WMS`、`Activity`、`AMS`,其關係如下圖 1. **`WMS`、`AMS` 其實是在同一個 Process (SystemServer)** 2. **`Session` 是 WMS 中的匿名 Binder,用來處理每個 View 的小需求** > 下圖紅代表 Binder Server、綠代表 Binder Client > > ![](https://i.imgur.com/R6njadR.png) ### WMS 組織 - ActivityRecord、WindowState * App 開啟一個 Activity 1. Activity 在 **AMS 對應的對象是 ActivityRecord**,**^\*^ ActivityRecord 在 WMS 中對應的是 AppWindowToken** > * WMS 也需要控制 AMS 中的 ActivityRecord 2. Activity 在 **WMS 對應的對象是 WindowState** > ![](https://i.imgur.com/BDNjnvZ.png) ### 窗口屬性 - Layer Type * Android 支援多種窗口,並且會將窗口分為三類,定義在 [**WindowManager#LayoutParams**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/WindowManager.java) 中 1. **Application Window**:普通 App 應用就是使用以下窗口類型 (列出幾種舉例),區間為 **1 ~ 99** 之間 | Value | 名稱 | 說明 | | -------- | -------- | -------- | | 1 | `FIRST_APPLICATION_WINDOW` | 應用類視窗的起始值 | | 1 | `TYPE_BASE_APPLICATION` | 其他應用都可覆蓋在該 Window 之上 | | 2 | `TYPE_APPLICATION` | 一般的應用窗口 | | 3 | `TYPE_APPLICATION_STARTING` | 應用程式啟動前的畫面 (用在加載某個畫面之前的預前畫面) | | 4 | `TYPE_DRAWN_APPLICATION` | 在繪畫完成前等待 | | 99 | `LAST_APPLICATION_WINDOW` | 應用視窗的結束值 | ```java= // WindowManager#LayoutParams public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable { public static final int FIRST_APPLICATION_WINDOW = 1; public static final int TYPE_BASE_APPLICATION = 1; public static final int TYPE_APPLICATION = 2; public static final int TYPE_APPLICATION_STARTING = 3; public static final int TYPE_DRAWN_APPLICATION = 4; public static final int LAST_APPLICATION_WINDOW = 99; ... } ``` 2. **Sub Window**:附加在應用之上的視窗,一般稱為 **子窗口**,其值區間為 **1000 ~ 1999** 之間 | Value | 名稱 | 說明 | | -------- | -------- | -------- | | 1000 | `FIRST_SUB_WINDOW` | SubWindow 的起始值 | | 1000 | `TYPE_APPLICATION_PANEL` | 該 SubWindow 顯示在依附的 App 之上 | | 1001 | `TYPE_APPLICATION_MEDIA` | 顯示有關 video 的 SubWindow | | 1002 | `TYPE_APPLICATION_SUB_PANEL` | 顯示在依附的 app 之上,**也在其他 SubWinow 之上** | | 1003 | `TYPE_APPLICATION_ATTACHED_DIALOG` | Dialog 類型的 Window | | 1004 | `TYPE_APPLICATION_MEDIA_OVERLAY` | 顯示在 `TYPE_APPLICATION_MEDIA ` 還有底下的應用視窗之間 | | 1005 | `TYPE_APPLICATION_ABOVE_SUB_PANEL` | 比 `TYPE_APPLICATION_SUB_PANEL` 更上層的 Window | | 1999 | `LAST_SUB_WINDOW` | SubWindow 的結束值 | ```java= // WindowManager#LayoutParams public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable { public static final int FIRST_SUB_WINDOW = 1000; public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW; public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1; public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2; public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3; public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4; public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5; ... public static final int LAST_SUB_WINDOW = 1999; } ``` 3. **System Window**:系統使用的 Window 視窗 (以下列出幾種),區間為 **2000 ~ 2999** 之間 | Value | 名稱 | 說明 | | -------- | -------- | -------- | | 2000 | `FIRST_SYSTEM_WINDOW` | System Window 的起始值 | | 2001 | `TYPE_STATUS_BAR` | Status bar 的 Window | | 2002 | `TYPE_SEARCH_BAR` | 搜尋 Bar 的 Window | | 2038 | `TYPE_APPLICATION_OVERLAY` | 覆蓋於其他 Activity 視窗 | | 2999 | `LAST_SYSTEM_WINDOW` | System Window 的結束值 | ```java= // WindowManager#LayoutParams public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable { public static final int FIRST_SYSTEM_WINDOW = 2000; public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW; public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1; ... public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38; ... public static final int LAST_SYSTEM_WINDOW = 2999; } ``` :::warning * 透過該值來決定 Window 應該要放置的層級 (Layer),但 **如果一個視窗中有多個相同層級的應用又該如何決定呢** ? > 看下一小節 ::: ### [WindowState](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/WindowState.java) 初始化 - 決定 Base Layer * 從 WindState 建構函數可以知道,Base Layer 數值可以從 WindowManagerPolicy#`getWindowLayerLw` 方法取得 :::info * 這邊也可以看到 1. Stub Window 的 Base Layer 取決於 Parent Window 2. BaseLayer 決定出來後要加上 10000 & 篇移值 1000 3. mSubLayer 決定子窗口應該要在父窗口的偏移植 ::: ```java= // WindowManagerPolicyConstants.java // 保留給多 Window (相同 Layer) int TYPE_LAYER_MULTIPLIER = 10000; // 保留給多 Window (相同 Layer),Z 軸偏移量 int TYPE_LAYER_OFFSET = 1000 // --------------------------------------------------------------- // WindowState.java final WindowManagerPolicy mPolicy; WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token, WindowState parentWindow, int appOp, WindowManager.LayoutParams a, ... 省略部分入參) { // 複製 `WindowManager.LayoutParams` mAttrs.copyFrom(a); ... mPolicy = mWmService.mPolicy; ... // 判斷目前 Window 是否是 SubWindow if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) { // 保留空間給多個相同 Layer 等級的 Window // @ 查看 getWindowLayerLw 方法 mBaseLayer = mPolicy.getWindowLayerLw(parentWindow) // 1. * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; // 2. // SubWindow mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type); ... } else { // @ 查看 getWindowLayerLw 方法 mBaseLayer = mPolicy.getWindowLayerLw(this) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; mSubLayer = 0; ... } } ``` 1. [**WindowManagerPolicy**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java) 透過傳入的 `WindowState` 決定 Window 基礎 Layer 層級 (數值越高,越接近使用者 & 上層) ```java= // WindowManagerPolicy.java default int getWindowLayerLw(WindowState win) { return getWindowLayerFromTypeLw(win.getBaseType(), win.canAddInternalSystemWindow()); } default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow) { return getWindowLayerFromTypeLw(type, canAddInternalSystemWindow, false /* roundedCornerOverlay */); } default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow, boolean roundedCornerOverlay) { // Always put the rounded corner layer to the top most. if (roundedCornerOverlay && canAddInternalSystemWindow) { return getMaxWindowLayer(); } // 1 ~ 99 之間 if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { return APPLICATION_LAYER; // APPLICATION_LAYER 是 2 } switch (type) { case TYPE_WALLPAPER: // wallpaper is at the bottom, though the window manager may move it. return 1; case TYPE_PRESENTATION: case TYPE_PRIVATE_PRESENTATION: case TYPE_DOCK_DIVIDER: case TYPE_QS_DIALOG: case TYPE_PHONE: return 3; case TYPE_SEARCH_BAR: return 4; case TYPE_INPUT_CONSUMER: return 5; case TYPE_SYSTEM_DIALOG: return 6; case TYPE_TOAST: // toasts and the plugged-in battery thing return 7; case TYPE_PRIORITY_PHONE: // SIM errors and unlock. Not sure if this really should be in a high layer. return 8; case TYPE_SYSTEM_ALERT: // like the ANR / app crashed dialogs // Type is deprecated for non-system apps. For system apps, this type should be // in a higher layer than TYPE_APPLICATION_OVERLAY. return canAddInternalSystemWindow ? 12 : 9; case TYPE_APPLICATION_OVERLAY: return 11; case TYPE_INPUT_METHOD: // on-screen keyboards and other such input method user interfaces go here. return 13; case TYPE_INPUT_METHOD_DIALOG: // on-screen keyboards and other such input method user interfaces go here. return 14; case TYPE_STATUS_BAR: return 15; ... 省略部分 case default: Slog.e("WindowManager", "Unknown window type: " + type); return 3; } } ``` 3. [**WindowManagerPolicy**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java)#`getSubWindowLayerFromTypeLw` 方法:決定子窗口在父窗口中的偏移量 :::info 從這裡可以看出來子窗口可能在 父窗口的上面、也能在下面 ::: ```java= // WindowManagerPolicy.java default int getSubWindowLayerFromTypeLw(int type) { switch (type) { case TYPE_APPLICATION_PANEL: case TYPE_APPLICATION_ATTACHED_DIALOG: return APPLICATION_PANEL_SUBLAYER; // 1 case TYPE_APPLICATION_MEDIA: return APPLICATION_MEDIA_SUBLAYER; // -2 case TYPE_APPLICATION_MEDIA_OVERLAY: return APPLICATION_MEDIA_OVERLAY_SUBLAYER; // -1 case TYPE_APPLICATION_SUB_PANEL: return APPLICATION_SUB_PANEL_SUBLAYER; // 2 case TYPE_APPLICATION_ABOVE_SUB_PANEL: return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER; // 3 } Slog.e("WindowManager", "Unknown sub-window type: " + type); return 0; } ``` ### 窗口屬性 - 決定最終 Layer 層級 * 詳細的 Window 視窗層級會在 WMS 中決定,**窗口數值越高,越接近 User** (有可能視窗中有 3 個 Window Type 是 TYPE_APPLICATION) ```java= // WindowManagerService.java public int relayoutWindow(Session session, IWindow client, LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, int flags, ... 省略部分入參) { ... int result = 0; boolean configChanged; final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); synchronized (mGlobalLock) { // WindowState 是 Activity 在 WMS 的代表 final WindowState win = windowForClientLocked(session, client, false); if (win == null) { return 0; } final DisplayContent displayContent = win.getDisplayContent(); final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); ... if (imMayMove) { displayContent.computeImeTarget(true /* updateImeTarget */); if (toBeDisplayed) { // 重新計算 Layer 層級 // @ 追蹤 assignWindowLayers 方法 displayContent.assignWindowLayers(false /* setLayoutNeeded */); } } return result; } ``` [**DisplayContent**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java)#**assignWindowLayers** 方法:準備重新計算該 Window 真正的 Layer 層級 ```java= // DisplayContent.java void assignWindowLayers(boolean setLayoutNeeded) { ... // @ 追蹤 assignChildLayers 方法 assignChildLayers(getSyncTransaction()); if (setLayoutNeeded) { setLayoutNeeded(); } ... } @Override void assignChildLayers(SurfaceControl.Transaction t) { assignRelativeLayerForIme(t, false /* forceUpdate */); // @ 追中 super.assignChildLayers super.assignChildLayers(t); } ``` 最終會呼叫到 [**WindowState**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/WindowState.java)#assignChildLayers 方法:WindowState 繼承於 [**WindowContainer**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java) 而其中會儲存該視窗的所有 Window,所以 `assignChildLayers` 行為會迭代所有的 Window 並重新賦予 layer 數值 ```java= // WindowContainer.java // 以 Z 軸儲存所有 Window protected final WindowList<E> mChildren = new WindowList<E>(); // ----------------------------------------------------------------- // WindowState.java public void assignChildLayers(Transaction t) { // Main Window 已經計算並保存好,而呼叫該 Function,代表該 View 應該要在 Main Window 之上 // `PRESERVED_SURFACE_LAYER ` 為 1 int layer = PRESERVED_SURFACE_LAYER + 1; // 迭代所有 Window 視窗 for (int i = 0; i < mChildren.size(); i++) { final WindowState w = mChildren.get(i); // `APPLICATION_MEDIA_OVERLAY` 比起 `APPLICATION_MEDIA` 還上層 if (w.mAttrs.type == TYPE_APPLICATION_MEDIA) { ... } else if (w.mAttrs.type == TYPE_APPLICATION_MEDIA_OVERLAY) { ... } else { // @ 追蹤 WindowState#assignLayer w.assignLayer(t, layer); } // 遞迴呼叫 assignChildLayers 方法 w.assignChildLayers(t); // layer 層級 + 1 layer++; } } ``` UML 關係如下 > ![](https://i.imgur.com/284qxeE.png) Struct 結構如下 > ![](https://i.imgur.com/PJdqrWA.png) * WindowContainer 就會直接透過 SurfaceControler 設定 Layer 層級 ```java= // WindowState.java @Override void assignLayer(Transaction t, int layer) { if (mStartingData != null) { // The starting window should cover the task. t.setLayer(mSurfaceControl, Integer.MAX_VALUE); return; } // See comment in assignRelativeLayerForImeTargetChild if (needsRelativeLayeringToIme()) { getDisplayContent().assignRelativeLayerForImeTargetChild(t, this); return; } // @ 追蹤 super.assignLayer super.assignLayer(t, layer); } // --------------------------------------------------------- // WindowContainer.java void assignLayer(Transaction t, int layer) { // 正在顯示則不調整 if (mTransitionController.isPlaying()) return; final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null; if (mSurfaceControl != null && changed) { // @ 查看 setLayer setLayer(t, layer); mLastLayer = layer; mLastRelativeToLayer = null; } } protected void setLayer(Transaction t, int layer) { if (mSurfaceFreezer.hasLeash()) { // When the freezer has created animation leash parent for the window, set the layer // there instead. mSurfaceFreezer.setLayer(t, layer); } else { // Route through surface animator to accommodate that our surface control might be // attached to the leash, and leash is attached to parent container. mSurfaceAnimator.setLayer(t, layer); } } // --------------------------------------------------------- // SurfaceFreezer.java void setLayer(SurfaceControl.Transaction t, int layer) { if (mLeash != null) { // 最終設定 Layer t.setLayer(mLeash, layer); } } ``` > ![](https://i.imgur.com/XqmmPgL.png) ### 窗口屬性 - LayoutParams * Window 的統一屬性設定在 [**WindowManager#LayoutParams**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/WindowManager.java) 中,並且有分為 `type`、`Flags` 兩種設定 1. **Type**:上面已經說過,主要有分為 `Application Window`、`Sub Window`、`System Window` 三種 2. **Flag**:莫認為 0,以下列出幾個 (有滿多的) | Value | Flag | 說明 | | - | -------- | -------- | | 0x00000001 | `FLAG_ALLOW_LOCK_WHILE_SCREEN_ON` | 只要該窗口可見,就允許鎖住屏幕 (可配合 `FLAG_KEEP_SCREEN_ON` 使用) | | 0x00000002 | `FLAG_DIM_BEHIND` | 窗口後的 Window 都變淡 | | 0x00000004 | `FLAG_BLUR_BEHIND` | 窗口後的 Window 變模糊 | | 0x00000008 | `FLAG_NOT_FOCUSABLE` | 該 Window 視窗不可事件,事件會直接傳遞給後方 Window | | 0x00000010 | `FLAG_NOT_TOUCHABLE` | 該窗口不接受任何事件 | | 0x00000080 | `FLAG_KEEP_SCREEN_ON` | 只要該窗口可見,就保持螢幕亮起 | 使用 Window#setFlags 就可以設置 ```java= Window w = activity.getWindow(); w.setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); ``` * System UI 控制:System UI 的設定定義在 [**View**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/View.java) 中,針對每個 View 個別設置 | Value | Flag | 說明 | | - | -------- | -------- | | 0 | `SYSTEM_UI_FLAG_VISIBLE` | 設定 System UI 可見 | | 0x00000002 | `SYSTEM_UI_FLAG_HIDE_NAVIGATION` | 設定 Navigtion Bar 不可見 | | 0x00000004 | `SYSTEM_UI_FLAG_FULLSCREEN` | 螢幕全屏 | | 0x00000100 | `SYSTEM_UI_FLAG_LAYOUT_STABLE` | 盡量保持 UI 布局的穩定性 | | 0x00000400 | `SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN` | 從螢幕 (0, 0) 開始繪製,也就是會從 Status Bar 開始繪製 | ## Activity 啟動 這裡用一張簡易的流程圖來回憶一下 Acitivty 的啟動流程 (僅列出重點),並從 ActivityRecord#**showStartingWindow** 繼續往下分析就可以看到 AMS 與 WMS 之間的關係 ![](https://i.imgur.com/IC3Y38C.png) ### [ActivityRecord](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/ActivityRecord.java) - showStartingWindow 啟動動畫 AnimationThread 使用 * 先來看一下 ActivityRecord 類的繼承關係,可以看到 **ActivityRecord 也是 WindowContainer 的子類** (WindowContainer 是泛型,可以包含多個 WindowContainer) ```java= // WindowContainer.java class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E> implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable { // 以 z 軸來儲存 Window 元素 protected final WindowList<E> mChildren = new WindowList<E>(); ... } ``` * 接這來看 Window 相關類的關係圖 > ![](https://i.imgur.com/qbummQT.png) * 在回到 Activity 啟動的過程中會使用中到 ActivityRecord#**showStartingWindow** 方法顯示 Window 啟動畫面 | Window 類型 | 說明 | | ---------------------------------- | ------------------------------------------------------------ | | `STARTING_WINDOW_TYPE_NONE` | 不添加 Starting Window | | `STARTING_WINDOW_TYPE_SNAPSHOT` | 當 APP 啟動後並切換到後台,Android 會替 APP 快照(類似截圖),當 APP 再次回到前景 (使用) 就會使用這個 | | `STARTING_WINDOW_TYPE_SPLASH_SCREEN` | 默認行為,與 APP 主題有關 | ```java= // ActivityRecord.java // 啟動視窗 StartingData mStartingData; void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch, boolean startActivity, ActivityRecord sourceRecord) { ... 省略部分 // @ 分析 addStartingWindow 函數 final boolean scheduled = addStartingWindow(packageName, resolvedTheme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, prev, newTask || newSingleActivity, taskSwitch, isProcessRunning(), allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty); ... debug 訊息 } boolean addStartingWindow(/* 省略參數 */) { ... 省略部分 // 若已經找 (啟動視窗) 到則不再尋找 if (mStartingData != null) { return false; } // 從 WindowContainer 中找到啟動視窗 // @ 查看 findMainWindow 類 final WindowState mainWin = findMainWindow(); if (mainWin != null && mainWin.mWinAnimator.getShown()) { // App already has a visible window...why would you want a starting window? return false; } // @ 查看 getStartingWindowType 方法 final int type = getStartingWindowType(newTask, taskSwitch, processRunning, allowTaskSnapshot, activityCreated, snapshot); ... 省略部分 // SplashScreenStartingData 實作 StartingData 抽象類 mStartingData = new SplashScreenStartingData(mWmService, pkg, resolvedTheme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, getMergedOverrideConfiguration(), typeParameter); // @ 繼續分析 scheduleAddStartingWindow 方法 scheduleAddStartingWindow(); return true; } // 透過 ActivityRecord 決定啟動 Window 類型 private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated, TaskSnapshot snapshot) { // 對照上面的表 if ((newTask || !processRunning || (taskSwitch && !activityCreated)) && !isActivityTypeHome()) { // 第一次啟動... return STARTING_WINDOW_TYPE_SPLASH_SCREEN; } else if (taskSwitch && allowTaskSnapshot) { if (isSnapshotCompatible(snapshot)) { return STARTING_WINDOW_TYPE_SNAPSHOT; } if (!isActivityTypeHome()) { return STARTING_WINDOW_TYPE_SPLASH_SCREEN; } return STARTING_WINDOW_TYPE_NONE; } else { return STARTING_WINDOW_TYPE_NONE; } } WindowState findMainWindow(boolean includeStartingApp) { WindowState candidate = null; for (int j = mChildren.size() - 1; j >= 0; --j) { final WindowState win = mChildren.get(j); final int type = win.mAttrs.type; // No need to loop through child window as base application and starting types can't be // child windows. if (type == TYPE_BASE_APPLICATION || (includeStartingApp && type == TYPE_APPLICATION_STARTING)) { if (win.mAnimatingExit) { candidate = win; } else { return win; } } } return candidate; } void scheduleAddStartingWindow() { if (StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) { // Debug 用 mAddStartingWindow.run(); } else { // 注意:我們真的想做 sendMessageAtFrontOfQueue() // 因為我們想在任何其他排隊的消息之前盡快處理消息。 if (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) { ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Enqueueing ADD_STARTING"); // mWmService 就是 WindowManagerService,而 mAnimationHandler 就是 Handler // @ mAnimationHandler 準備分析 mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow); } } } ``` * 在這裡可以看到 ActivityRecord 會將自己的 Runnable 透過 WMS#mAnimationHandler (就是 Handler) 推到 **AnimationThread 的線程** > `mAddStartingWindow` 下個小節說明 ```java= // WindowManagerService.java final Handler mAnimationHandler = new Handler( AnimationThread.getHandler().getLooper() ); ``` > ![](https://i.imgur.com/coRLsig.png) ### [ActivityRecord](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/ActivityRecord.java) - AddStartingWindow 添加啟動視窗 * 先來查看一下 ActivityRecord 推送到 WMS#**mAnimationHandler** 的 Runnable 任務 ```java= // ActivityRecord.java private final AddStartingWindow mAddStartingWindow = new AddStartingWindow(); // 複習一下~ void scheduleAddStartingWindow() { if (StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) { ... 省略 } else { // 注意:我們真的想做 sendMessageAtFrontOfQueue() // 因為我們想在任何其他排隊的消息之前盡快處理消息。 if (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) { ... Log 訊息 mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow); } } } // @ 分析 AddStartingWindow 類 private class AddStartingWindow implements Runnable { // run 會被 WMS#AnimationHandler 線程呼叫 @Override public void run() { // Can be accessed without holding the global lock final StartingData startingData; synchronized (mWmService.mGlobalLock) { // There can only be one adding request, silly caller! if (!StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) { // Debug 用 mWmService.mAnimationHandler.removeCallbacks(this); } if (mStartingData == null) { // 動畫被取消 ... 省略 Log 訊息 return; } startingData = mStartingData; } ... 省略 Log 訊息 WindowManagerPolicy.StartingSurface surface = null; try { // createStartingSurface 是抽象方法,具體邏輯由子類完成 // 目前來說就是使用 ++SplashScreenStartingData 類++ 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 the window was successfully added, then we need to remove it. 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) { ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Added starting %s: startingWindow=%s startingView=%s", ActivityRecord.this, mStartingWindow, mStartingSurface); } } if (abort) { surface.remove(false /* prepareAnimation */); } } else { ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Surface returned was null: %s", ActivityRecord.this); } } } ``` * StartingData 是抽象類,而在目前的狀況,實作者是 [**SplashScreenStartingData 類**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/SplashScreenStartingData.java) (addStartingWindow 內創建的對象就是 SplashScreenStartingData) ```java= // SplashScreenStartingData.java class SplashScreenStartingData extends StartingData { ... 省略部分 SplashScreenStartingData(WindowManagerService service, String pkg, int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, Configuration mergedOverrideConfiguration, int typeParams) { super(service, typeParams); ... 省略部分 } @Override StartingSurface createStartingSurface(ActivityRecord activity) { // 1. mService 就是 WindowManagerService // 2. mStartingSurfaceController 就是 StartingSurfaceController return mService.mStartingSurfaceController.createSplashScreenStartingSurface( activity, mPkg, mTheme, mCompatInfo, mNonLocalizedLabel, mLabelRes, mIcon, mLogo, mWindowFlags, mMergedOverrideConfiguration, activity.getDisplayContent().getDisplayId()); } @Override boolean needRevealAnimation() { return true; } } ``` * 呼叫 [**StartingSurfaceController**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/StartingSurfaceController.java) 類的 `createSplashScreenStartingSurface` 方法,這個方法會呼叫到 WMS 中的 mPolicy (**也就是 [PhoneWindowManager 類](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/policy/PhoneWindowManager.java)**) ```java= // StartingSurfaceController.java StartingSurface createSplashScreenStartingSurface(ActivityRecord activity, String packageName, int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) { if (!DEBUG_ENABLE_SHELL_DRAWER) { // WMS 中的 mPolicy 實作類是 PhoneWindowManager return mService.mPolicy.addSplashScreen(activity.token, activity.mUserId, packageName, theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, overrideConfig, displayId); } synchronized (mService.mGlobalLock) { final Task task = activity.getTask(); if (task != null && mService.mAtmService.mTaskOrganizerController.addStartingWindow( task, activity, theme, null /* taskSnapshot */)) { return new ShellStartingSurface(task); } } return null; } ``` > ![](https://i.imgur.com/kUEkRmR.png) ### ViewRootImpl - 繪製 View * 回顧一下:我們目前是在添加 Activity 啟動 Window 的路上,前面幾個步驟介紹了 AMS 對 WMS 添加 Activity 的啟動畫面 **showStartingWindow** ~ * 透過 [**PhoneWindowManager**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/policy/PhoneWindowManager.java)#`addSplashScreen` 會創建 PhoneWindow 對象,並設定 Window 的 WindowManager#LayoutParams 屬性,之後在取得 WindowManager 後呼叫 addView 1. 創建 PhoneWindow 對象 (Window 的實作類) 2. 設定 Window 對象的 WindowManager#LayoutParams (預設 type 類型是 Application) 3. **將 Activity#appToken ==也就是 App 的 ApplicationThread==** 賦予給 WMS,方便之後調用 4. 透過 context#getSystemService 取得 WMS 的代理類,並呼叫 WMS#addView 方法 (其實是透過 Session 通知 WMS 添加 Window) ```java= // PhoneWindowManager.java @Override public StartingSurface addSplashScreen(IBinder appToken, int userId, String packageName, int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) { ... 省略部分 WindowManager wm = null; View view = null; try { ... 省略部分 // 484 很孰悉 ~ ? 在分析 xml 創建 View 的時候也會看到它 final PhoneWindow win = new PhoneWindow(context); // 創建 PhoneWindow ... 省略部分 // 設定 Window 用的 LayoutParams win.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT); final WindowManager.LayoutParams params = win.getAttributes(); // 設定 LayoutParams // appToken 是 App 的 ApplicationThread params.token = appToken; ... 省略部分 addSplashscreenContent(win, context); // 取得 WindowManager wm = (WindowManager) context.getSystemService(WINDOW_SERVICE); // 取得 Decor View (沒有的話就創建) view = win.getDecorView(); ... 省略 Log wm.addView(view, params); // 呼叫 WMS#addView !! return view.getParent() != null ? new SplashScreenSurface(view, appToken) : null; } /* catch、finally */ return null; } ``` * 而 WindowManager 是 interface,它的實作類是 [**WindowManagerImpl**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/WindowManagerImpl.java),在其中有一個 WindowManagerGlobal 對象 > WindowManagerGlobal 是一個進程單例對象,WindowManagerGlobal 就是一個 **橋接模式設計**,也是透過它來與 WMS 通訊 ```java= // WindowManagerImpl.java public final class WindowManagerImpl implements WindowManager { private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); @Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyTokens(params); // @ 查看 addView 方法 mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow, mContext.getUserId()); } } ``` > ![](https://i.imgur.com/Wxcf8rB.png) * 最後呼叫到 [**WindowManagerGlobal**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/WindowManagerGlobal.java)#addView 方法,WindowManagerGlobal 類會管理該 ++進程中所有的 ViewRootImpl & Window & LayoutParams++ ```java= // WindowManagerGlobal.java private final Object mLock = new Object(); // 進程中所有的 View private final ArrayList<View> mViews = new ArrayList<View>(); private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>(); public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow, int userId) { // 基礎判空 if (view == null) { throw new IllegalArgumentException("view must not be null"); } if (display == null) { throw new IllegalArgumentException("display must not be null"); } if (!(params instanceof WindowManager.LayoutParams)) { throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); } final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; // 父 Window 判斷 if (parentWindow != null) { parentWindow.adjustLayoutParamsForSubWindow(wparams); } else { // If there's no parent, then hardware acceleration for this view is // set from the application's hardware acceleration setting. final Context context = view.getContext(); if (context != null && (context.getApplicationInfo().flags & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) { wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; } } ViewRootImpl root; View panelParentView = null; // 添加 View 的流程使用 鎖 synchronized (mLock) { ... 省略部分 // 創建 ViewRootImpl 對象 root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); // do this last because it fires off messages to start doing things try { // 準備繪製 root.setView(view, wparams, panelParentView, userId); } /* 省略 catch */ } } ``` > ![](https://i.imgur.com/Xx31zKa.png) * 步驟到 [**ViewRootImpl**](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewRootImpl.java) 後有兩個重點 1. 透過 ViewRootImpl#mWindow (內部類 W) 與 WMS 通訊 WindowSession 也是一個系統服務 成員 mWindow 在 ViewRootImpl 創建時被賦予,並且 mWindow 是 ViewRootImpl 的內部類,它繼承 IWindow.Stub (作為 Binder 的 Server 端) > **W 它是 WMS 調用客戶端的接口** (之後當 Window 產生變化時會通知IWindow.Stub) ```java= // ViewRootImpl.java final W mWindow; public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session, boolean useSfChoreographer) { ... 省略 mWindow = new W(this); } static class W extends IWindow.Stub { W(ViewRootImpl viewAncestor) { mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor); mWindowSession = viewAncestor.mWindowSession; } ... 省略 } ``` 2. 與 WMS 通訊成功後,就會進入 View 的繪製流程 (執行 onMeasure、onLayout、onDraw) ```java= // ViewRootImpl.java public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { setView(view, attrs, panelParentView, UserHandle.myUserId()); } public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) { synchronized (this) { if (mView == null) { mView = view; ... 省略部分 requestLayout(); ... 省略部分 } } } @Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; // 分析 scheduleTraversals(); } } final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } } final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; // 將 mTraversalRunnable 任務添加到 Handler 中 mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } } void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; ... 省略部分 performTraversals(); ... 省略部分 } } // 該函數很長,只會列出幾個我們常見的要點 (其中包含了本地的繪製,WMS 通訊...) private void performTraversals() { ... 省略部分 try { ... 省略部分 // 參數放入 mWindow,之後讓 WMS 作回調 ~ !!! // WindowSession 也是一個系統服務 // @ 分析 addToDisplayAsUser res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), userId, mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets, mTempControls); ... 省略部分 } /* 省略 catch、finally */ if (mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params != null || mForceNextWindowRelayout) { ... 省略部分 if (!mStopped || wasReportNextDraw) { // 會執行 onMeasure performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); ... 省略部分 } } ... 省略部分 if (didLayout) { // 會執行 onLayout performLayout(lp, mWidth, mHeight); } ... 省略部分 if (!cancelDraw) { performDraw(); } ... 省略部分 } ``` :::info * WindowSession 也是一個系統服務 不過它是一個匿名服務,必須透過 WMS 才可以取得 WindowSession 對象 ::: > ![](https://i.imgur.com/F50hDuH.png) ### 透過 WMS 取得 [Session](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/Session.java) - addToDisplayAsUser * 先來看一下 ViewRootImpl 的 Session 是如何取得 > 透過 WindowManagerGlobal 取得 Session ```java= // ViewRootImpl.java public ViewRootImpl(Context context, Display display) { // 透過 WindowManagerGlobal 取得 Session (Session 的代理類) // @ 追 getWindowSession 方法 mWindowSession = WindowManagerGlobal.getWindowSession(); ... 省略部分 } // 複習一下上面的調用 performTraversals 方法的重點 private void performTraversals() { ... 省略部分 try { ... 省略部分 // 透過 mWindow 與 WMS 通訊 // WindowSession 也是一個系統服務 res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), userId, mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets, mTempControls); ... 省略部分 } /* 省略 catch、finally */ ... 省略部分 } ``` * 從上面看到 Session 的取得是透過 WindowManagerGlobal 類:而 WindowManagerGlobal 則是透過 ^1.^ ServiceManager 取得 WMS,^2.^ 再透過創建 Session 給客戶端 ```java= // WindowManagerGlobal.java public static IWindowSession getWindowSession() { synchronized (WindowManagerGlobal.class) { if (sWindowSession == null) { try { InputMethodManager imm = InputMethodManager.getInstance(); // 追 getWindowManagerService 方法 IWindowManager windowManager = getWindowManagerService(); // 使用 WMS 間接取得 Session 服務,查看 openSession 方法 sWindowSession = windowManager.openSession( new IWindowSessionCallback.Stub() { @Override public void onAnimatorScaleChanged(float scale) { ValueAnimator.setDurationScale(scale); } }, imm.getClient(), imm.getInputContext()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return sWindowSession; } } public static IWindowManager getWindowManagerService() { synchronized (WindowManagerGlobal.class) { if (sWindowManagerService == null) { // 1. 透過 ServiceManager 取得 sWindowManagerService = IWindowManager.Stub.asInterface( ServiceManager.getService("window")); try { if (sWindowManagerService != null) { ValueAnimator.setDurationScale( sWindowManagerService.getCurrentAnimatorScale()); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return sWindowManagerService; } } ``` * 在這邊就可以看到,**Session 是一個 ++匿名服務++**,它不能透過 ServiceManger 直接取得,**==Session 必須透過 WMS 取得該服務==,每個 ViewRootImpl 對應一個 Session 服務** > 同時一個 View 也對應了一個 ViewRootImpl,也就是說一個 Session 對應一個 View ```java= // WindowManagerService.java @Override public IWindowSession openSession(IWindowSessionCallback callback) { // 每個 ViewRootImpl 對應一個 Session 服務 return new Session(this, callback); } ``` > ![](https://i.imgur.com/fuatshg.png) * 最後透過 Session#addToDisplayAsUser 來訪問 WMS#addWindow 方法 ```java= class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { final WindowManagerService mService; @Override public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities, InputChannel outInputChannel, InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) { // @ 呼叫到 WindowManagerService#addWindow 方法 // window -> ViewRootImpl 中的 W 類 return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId, requestedVisibilities, outInputChannel, outInsetsState, outActiveControls); } } ``` > ![](https://i.imgur.com/iqAP4Kp.png) ## [WindowManagerService](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/WindowManagerService.java) WindowManagerService 簡單理解起來是掌管該裝置所有的 Window 的上下關係 (z 軸的管理)、傳輸繪製任務到底層 > 先介紹 [**WindowManagerService**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/WindowManagerService.java) 幾個比較重要的成員變量 | 變量 | 說明 | 補充 | | - | - | - | | mPolicy : WindowManagerPolicy | 定義一個窗口策略所須遵循的 **++通用規範++**,並提供了 WindowManger 特定的 UI 行為 | WindowManager 是 interface,它的實作是 **PhoneWindowManager** | | mSessions : ArraySet<Session\> | 它主要用於進程通訊,**其他的應用進程的 View 想要和 WMS 通訊,都要通過 Session 與 WMS 通訊** | 每個應用進程是透過 **WindowManagerGlobal 取得 Session** | | **mWindowMap** : WindowHashMap | WindowHashMap 繼承 HashMap,**用來保存所有 ViewImpl**;Key: IBinder、Value: WindowState | Key 其實是 IWindow (ViewRootImpl 的內部類 W)、Value 是保存窗口訊息 | | mFinishedStarting : ArrayList<AppWindowToken\> | AppWindowToken 的父類是 WindowToken 當 APP 要向 WMS 申請一個新窗口時就要給予這個 token 讓 WMS 驗證 | **每個 Activity 對應一個 AppWindowToken (一個 AppWindowToken 管理多個 WindowToken 視窗)** | | mResizingWindows : ArrayList<WindowState\> | 用來儲存正在調整大小的視窗 | | | mAnimator : WindowAnimator | 管理 Window 動畫 & 特效 | | | mH : H | H 是 Handler 類型,它是屬於 **==DisplayThread== Looper** | 將消息給 WMS | | mInputManager : InputManagerService | IMS 系統服務,當使用者有輸入訊息時,WMS 會給予一個適合的視窗 | | > ![](https://i.imgur.com/YEG17Yp.png) ### [WMS](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/wm/WindowManagerService.java) - addWindow 方法 * 上面有提及,APP 在添加視窗時會透過 WindowManagerGlobal#Session,而系統 Session 又會呼叫 WMS#**addWindow** 方法,由於該方法較長,以下分為 3 個階段來看 1. 檢查、取得 **DisplayContent**、判斷是否是子視窗 1. 檢查啟動權限:是否是系統窗口 & 使用者設定的權限... 等等 2. 取得 DisplayContent (該 WindowState 應該放置的視窗),判斷是否重複添加 3. 判斷是否是子視窗 (`FIRST_SUB_WINDOW` <= type <= `LAST_SUB_WINDOW`) 4. 從 mWindowMap 嘗試取得父窗口,如果目前視窗是子窗口,但 ParentWindow 取出的也是子窗口,就會返回錯誤 (ADD_BAD_SUBWINDOW_TOKEN) 5. 判斷 WindowManager 的各種 Type 類型,做相應的檢查處理 ```java= // WindowManagerService.java public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility, int displayId, /*省略部分參數*/ ) { ... 省略部分 // 1. 檢查啟動權限:是否是系統窗口 & 使用者設定的權限... 等等 (傳入 type... 判斷) int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName, appOp); if (res != ADD_OKAY) { return res; } WindowState parentWindow = null; ... 省略部分 synchronized (mGlobalLock) { if (!mDisplayReady) { throw new IllegalStateException("Display has not been initialialized"); } // 2. 取得 DisplayContent (該 WindowState 應該放置的視窗) final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token); if (displayContent == null) { ProtoLog.w(WM_ERROR, "Attempted to add window to a display that does " + "not exist: %d. Aborting.", displayId); return WindowManagerGlobal.ADD_INVALID_DISPLAY; } ... 省略部分 // 3. 判斷窗口類型:當前是判斷是否是子視窗 if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) { // 4. 如果是子窗口,嘗試取得父窗口 (attrs 是 LayoutParams) parentWindow = windowForClientLocked(null, attrs.token, false); // 判斷是否有 ParentWindow if (parentWindow == null) { return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; } // 並且 Parent 視窗不可以是子視窗 if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) { return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; } } ... 省略部分 } } ``` > ![](https://i.imgur.com/QdMP943.png) 2. **取得 Activity 的 AppWindowToken** 1. 若有 Parent 窗口,那它會與子窗口有相同的 Token (IBinder),也就是說 ParentWindow & SubWindow 有相同的 Token 2. 設定窗口類型 (如果有 ParentWindow,則使用 ParentWindow 的 type) 3. 依照狀況 **取得 WindowToken**,細節請看註解,這裡有多個 token 相關關鍵字容易混淆,我們以下表區分 | Token | 說明 | 補充 | | -------- | -------- | - | | `token` | 宣告為 WindowToken 類型 | | | `attrs.token` | 類型為 IBinder,代表該窗口的主人 | 如果有 Parent Window 窗口主人就轉為 Parent | | | | | | | | | > ![](https://i.imgur.com/LBWtRTN.png) ```java= // WindowManagerService.java public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility, int displayId, /*省略部分參數*/ ) { ... 省略部分 synchronized (mGlobalLock) { ActivityRecord activity = null; final boolean hasParent = parentWindow != null; // 1. 若有 Parent 窗口,那它會與子窗口有相同的 Token WindowToken token = displayContent.getWindowToken( hasParent ? parentWindow.mAttrs.token : attrs.token); // 2. 設定窗口類型 (如果有父窗口,則使用父窗口的 type) final int rootType = hasParent ? parentWindow.mAttrs.type : type; boolean addToastWindowRequiresToken = false; final IBinder windowContextToken = attrs.mWindowContextToken; // 若是沒有 token 會依照情況做創建 if (token == null) { if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type, rootType, attrs.token, attrs.packageName)) { return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } // 如果有 Parent 就優先使用 Parent 的 token if (hasParent) { // Use existing parent window token for child windows. token = parentWindow.mToken; } else if (mWindowContextListenerController.hasListener(windowContextToken)) { ... 省略部分 } else { final IBinder binder = attrs.token != null ? attrs.token : client.asBinder(); // 如果 AMS 中沒有 Token 就會創建一個 // 創建 WindowToken token = new WindowToken.Builder(this, binder, type) .setDisplayContent(displayContent) .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow) .setRoundedCornerOverlay(isRoundedCornerOverlay) .build(); } } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) { //應用類型窗口 // 從 token 中取得 ActivityRecord activity = token.asActivityRecord(); ... 判斷是否可取到 Activity 等等,無法取得則返回錯誤 } else if (...) { ... 省略其他判斷 } ... 省略部分 } } ``` > ![](https://i.imgur.com/Uqjv4bB.png) 如果是應用窗口,可以發現 WMS 儲存的 Token 與 AMS 中的 AcitivtyRecord 有關係 ```java= // 從 token 中取得 ActivityRecord activity = token.asActivityRecord(); ``` > ![](https://i.imgur.com/GCvRLS7.png) 3. 保存 Window 狀態、Client 訊息、判斷顯示視窗、做調整 1. **==創建 WindowState==**,保存 Window 的所有訊息狀態 2. 判斷 Client 視窗狀態 3. 檢查顯示視窗 4. 依照情況修正 LayoutParams 5. 緩存資料在 WMS 中 * `mWindowMap` 集合添加 IWindow 視窗 * `WindowToken` 集合添加到 WindowStete 內 6. 透過 DisplayPolicy#**addWindowLw** 添加 Window 到系統中 7. **使用 `assignChildLayers` 方法,==重新分配 Layer 層級==** ```java= // WindowManagerService.java public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility, int displayId, /*省略部分參數*/ ) { ... 省略部分 synchronized (mGlobalLock) { ... 省略部分 // 1. 保存 Window 的所有訊息狀態 (View 的狀態) // 存入相對應的 token (WindowToken) final WindowState win = new WindowState(this, session, client, token, parentWindow, appOp[0], attrs, viewVisibility, session.mUid, userId, session.mCanAddInternalSystemWindow); if (win.mDeathRecipient == null) { // 2. 客戶端已經死亡,不必繼續往下執行 return WindowManagerGlobal.ADD_APP_EXITING; } // 3. 檢查顯示視窗 if (win.getDisplayContent() == null) { ... log msg return WindowManagerGlobal.ADD_INVALID_DISPLAY; } final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); // 4. 依照情況修正 LayoutParams displayPolicy.adjustWindowParamsLw(win, win.mAttrs); win.setRequestedVisibilities(requestedVisibilities); attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), callingUid, callingPid); // 檢查 Type 是否合法 res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid); if (res != ADD_OKAY) { return res; } ... 省略部分 win.attach(); // WindowState#attach // 5. mWindowMap 添加 IWindow 視窗 mWindowMap.put(client.asBinder(), win); // View Binder(ViewRootImpl#W) 作為 Key,存入 State ... 省略部分 // 5. WindowStete 添加到 WindowToken 內 win.mToken.addWindow(win); // 6. 將 Window 加入系統中 displayPolicy.addWindowLw(win, attrs); ... 省略部分 // 7. 重新分配 Layer 層級 win.getParent().assignChildLayers(); } Binder.restoreCallingIdentity(origId); return res; } ``` > ![](https://i.imgur.com/loS8xQ4.png) * 總結一下 addWindow 方法 1. 對添加的 Window 進行檢查 2. WindowToken 相關處理,如果可以創建的話就由 WMS 創建,或是有 ParentWindow 也可以直接使用 > 應用視窗 ActivityRecord 也可以看做 Token 3. WindowState 保存每個 IWindow (ViewRootImpl),並將 WindowState 存入 WindowToken 4. 創建 & 配置 DisplayContent,完成窗口添加到系統的準備工作 ### WMS 刪除 Window // TODO: ## Appendix & FAQ :::info 參考 https://ljd1996.github.io/2020/08/27/Android-Window%E6%9C%BA%E5%88%B6%E5%8E%9F%E7%90%86/ ::: ###### tags: `Android Framework`