---
# System prepended metadata

title: WindowManager 和 Dialog
tags: [Android Framework]

---

---
title: 'WindowManager 和 Dialog'
disqus: kyleAlien
---

WindowManager 和 Dialog
===

## OverView of Content
> 可配合 [**Builder 模式**](https://hackmd.io/rLN00StPRmqok3zPzIsxjA?view#Android-Source-Builder-%E7%A0%94%E7%A9%B6)對 AlertDialog 的創建一起看，研究 Dialog 的 show 函數如何新增畫面在界面上


[TOC]

## WindowManager 概述
不只 Dialog 的畫面、Activity 的畫面也是透過 WindowManager 來加載到螢幕上

### WindowManager 代理
* WindowManager 是 APP 應用端 對系統服務 WMS 的代理，如同在 [**LayoutInflater**](https://hackmd.io/EPfgb14TS-Kcd-Pv7tDZ-w?view#LayoutInflate-%E6%A6%82%E8%BF%B0) 中所提到的，**WMS 服務也是透過 SystemServiceRegistry 加載**，**==WINDOW_SERVICE==**

* 傳入 Context 建立 WindowManagerImpl 物件 (下面會看到不同的差異)
    ```java=
    // SystemServiceRegistry.java
    static {
        ...
        registerService(Context.WINDOW_SERVICE, WindowManager.class,
                    new CachedServiceFetcher<WindowManager>() {
                @Override
                public WindowManager createService(ContextImpl ctx) {
                    return new WindowManagerImpl(ctx);
                }});
        ...
    }

    // --------------------------------------------------------------------
    // WindowManagerImpl.java

        public WindowManagerImpl(Context context) {
            this(context, null);
        }

        private WindowManagerImpl(Context context, Window parentWindow) {
            mContext = context;
            mParentWindow = parentWindow;
        }
    ```

## Dialog 視窗
我們知道 Context 的實現類是 [**ContextImpl**](https://hackmd.io/qhIoWcSZQ-GzL3G_Sgqb0A#Context-amp-ContextImpl) 類，可以**透過 Context 的 getSystemService 來獲得具體服務 (系統服務進程) 的代理**

Dialog 就在建構函數時獲取 WindowManager 服務

### Dialog 取得 - WindowManager 服務
* Dialog constructor 建構函數：會準備 WindowManager、PhoneWindow 對象

    | 類 | 功能概述 |
    | -------- | -------- |
    | WindowManager | 添加 Dialog 視窗 |
    | PhoneWindow | 加載 xml 布局 |

    ```java=
    /** 
     * Dialog.java
     */ 
    public class Dialog implements DialogInterface, Window.Callback,
            KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback {

        private final WindowManager mWindowManager;
        
        final Window mWindow;    // 實作是 PhoneWindow 類

        // Dialog construct
        Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
            
            ... 準備 context

            // 發現 Dialog 建構函數在獲取服務
            mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            // 自己建構 PhoneWindow 類
            final Window w = new PhoneWindow(mContext);
            mWindow = w;

            w.setCallback(this);
            w.setOnWindowDismissedCallback(this);
            
            // @ 追蹤 setWindowManager 方法
            // 讓 Window & WindowManager 產生關係!!!
            w.setWindowManager(mWindowManager, null, null);
            w.setGravity(Gravity.CENTER);

            mListenersHandler = new ListenersHandler(this);
        }        
    }
    ```
    > ![](https://i.imgur.com/56rwhRo.png)


* **透過 [Window](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/Window.java)#setWindowManager 方法：讓 Window (實作類是 PhoneWindow) & WindowManager 產生了關聯**
    ```java=
    /**
     * Window.java (抽象類)
     */ 
        private WindowManager mWindowManager;

        public void setWindowManager(WindowManager wm,     
                                     IBinder appToken,     // null
                                     String appName) {    // null 
            setWindowManager(wm, appToken, appName, false);
        }

        public void setWindowManager(WindowManager wm, 
                                     IBinder appToken,    // null
                                     String appName,     // null
                boolean hardwareAccelerated) {
            mAppToken = appToken;
            mAppName = appName;
            mHardwareAccelerated = hardwareAccelerated
                    || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);

            // 如果沒有則再次獲取 Window 服務
            if (wm == null) {
                wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
            }
            // @ 追蹤 createLocalWindowManager 方法
            // 建立 WindowManagerImpl
            mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
        }


    ```
* [**WindowManagerImpl**](**https://**)#**createLocalWindowManager** 這個方法中與 `WINDOW_SERVICE 服務` 都是創建  WindowManagerImpl 對象，差別是 **createLocalWindowManager 將傳入的 Window 設定為 parentWindow**

    > 這裡的 parentWindow 就是 Dialog 的 PhoneWindow 類

    > 並可以看出 Window & WindowManagerImpl 相互持有
    > ![](https://i.imgur.com/rKq49pU.png)

    :::info
    * 與 Context 建立的 WindowManagerImpl 比起來，Context 的 WindowManagerImpl 尚未與 Window 產生關聯
    :::

    ```java=
    // WindowManagerImpl.java

    public final class WindowManagerImpl implements WindowManager {
        
        private final Window mParentWindow;

        // 子系統服務的呼叫，並沒有 parentView
        public WindowManagerImpl(Context context) {
            this(context, null /* parentWindow */, null /* clientToken */);
        }

        public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
            return new WindowManagerImpl(mContext, parentWindow);
        }

        
        private WindowManagerImpl(Context context, Window parentWindow,
                @Nullable IBinder windowContextToken) {
            mContext = context;
            mParentWindow = parentWindow;
            mWindowContextToken = windowContextToken;
        }
    }
    ```

    > ![](https://i.imgur.com/JRVGCea.png)

### WindowManager addView - 連接 [**WindowManagerGlobal**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/WindowManagerGlobal.java)
* 在 Dialog 中的 show 方法最後會使用 **WindowManager#addView**，從上面我們會知道它調用到 WindowManagerImpl#**addView**

    ```java=
    // Dialog.java

        public void show() {
            ... 省略部份
                
            // @ mWindow 就是 PhoneWindow
            mDecor = mWindow.getDecorView();

            ... 省略部份

            WindowManager.LayoutParams l = mWindow.getAttributes();
            
            ...

            // @ 追蹤 addView 方法
            mWindowManager.addView(mDecor, l);
            
            ...
        }

    ```
    
* WindowManagerImpl#addView 其實會調用到 [**WindowManagerGlobal**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/WindowManagerGlobal.java)#addView 方法
    ```java=
    /**
     *  WindowManagerImpl,java
     */
    public final class WindowManagerImpl implements WindowManager {
        
        // 進程單例
        private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
        private final Context mContext;
        
        // 目前是 Dialog#PhoneWindow
        private final Window mParentWindow;

        @Override
        public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
            applyTokens(params);
            mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
        }
        
        
        private void applyTokens(@NonNull ViewGroup.LayoutParams params) {
            // token 其實就是 WindowManager.LayoutParams
            if (!(params instanceof WindowManager.LayoutParams)) {
                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
            }
            
            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
            assertWindowContextTypeMatches(wparams.type);
            // Only use the default token if we don't have a parent window and a token.
            if (mDefaultToken != null && mParentWindow == null && wparams.token == null) {
                wparams.token = mDefaultToken;
            }
            wparams.mWindowContextToken = mWindowContextToken;
        }

    }
    ```
    
    從關係圖上可以看出 WindowManagerImpl 是一個橋接類，它聯繫著 Window & WindowManagerGlobal 類
    > ![](https://i.imgur.com/DdSVLsN.png)


### WindowManagerGlobal - 創建 ViewRootImpl 

* WindowManagerGlobal#addView 方法會創建一個 ViewRootImpl 對象

    ```java=
    /**
     * WindowManagerGlobal,java
     */ 
    public final class WindowManagerGlobal {
        ...

        private static WindowManagerGlobal sDefaultWindowManager;
        
        private final ArrayList<View> mViews = new ArrayList<View>();
        
        private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
        
        private final ArrayList<WindowManager.LayoutParams> mParams =
                new ArrayList<WindowManager.LayoutParams>();

        private WindowManagerGlobal() { }

        // 單例
        public static WindowManagerGlobal getInstance() {
            // 使用 - 類鎖  同步機制
            synchronized (WindowManagerGlobal.class) {
                if (sDefaultWindowManager == null) {
                    sDefaultWindowManager = new WindowManagerGlobal();
                }
                return sDefaultWindowManager;
            }
        }

        public void addView(View view, ViewGroup.LayoutParams params,
                Display display, Window parentWindow) {

            // 忽略判空、檢查...

            if (!(params instanceof WindowManager.LayoutParams)) {
                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
            }

            // Window 布局參數
            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;

            if (parentWindow != null) {
                // 目前有 parentWindow
                parentWindow.adjustLayoutParamsForSubWindow(wparams);
            } else {
                
                // 沒有 parentWindow 設定硬體加速
                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;

            // mLock 普通對象鎖
            synchronized (mLock) {
                // Start watching for system property changes.
                ...

                // @ 建立 ViewRootImpl 對象 
                root = new ViewRootImpl(view.getContext(), display);
                // 設定布局參數
                view.setLayoutParams(wparams);

                // @ 以下都是 ArrayList 對象
                mViews.add(view);			// View
                mRoots.add(root);			// ViewRootImpl
                mParams.add(wparams);		// WindowManager.LayoutParams 視窗參數
            }

            //最後執行此操作，因為它會觸發傳遞消息
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                ...
            }
        }
    ```
    這邊先跳過 Binder 與 WMS 的進程通訊，先知道 WindowManagerGlobal#addView 行為 **會觸發到 ++[ViewRootImpl](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/ViewRootImpl.java)#setView++ 方法**

* 分析完畢 View 布局後，在 WindowManagerGlobal 中的 addView 方法主要做了下面 4 件事情
    1. **新建 ViewRootImpl 對象**
    
    2. 將 Window 布局參數 Params 設定給 View
    
    3. 儲存 View、ViewRootImpl、Params 到 WindowManagerGlobal#ArrayList 中
    
    4. **透過 ViewRootImpl 的 setView 方法設定 View 顯示到視窗上**

* WindowManagerGlobal & WindowManagerImpl 關係圖
    > ![](https://i.imgur.com/H5JTp2s.png)

## ViewRootImpl
ViewRootImpl 並不是 View 類，負責與 Native 通訊，取得繪畫範圍，並驅動 App 進程繪圖


### [ViewRootImpl](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/ViewRootImpl.java) 類 - 取得 WindowSession 服務

* 從上面 WindowManagerGlobal#addView 會發現 **++每一個新的 View 都會建立一個 ViewRootImpl 對象++**，**其實它本身並不是 View 類，它是 ==Framework 層與 native 層的交接==**
    ```java=
    // ViewRootImpl.java

    public final class ViewRootImpl implements ViewParent,
            View.AttachInfo.Callbacks,
            ThreadedRenderer.HardwareDrawCallbacks {
            ...

            public ViewRootImpl(Context context, Display display) {
                mContext = context;

                // @ 追蹤 getWindowSession 方法
                // 獲取 WindowSession，與 WMS 建立連接，往下追 getWindowSession
                mWindowSession = WindowManagerGlobal.getWindowSession();
                mDisplay = display;
                mBasePackageName = context.getBasePackageName();
                // "1. "
                // 儲存目前的 Thread
                mThread = Thread.currentThread();
                ...
            }

    }
    ```
    :::success
    * 更新 View 的 Thead，**在 ViewRootImpl 有紀錄 Thread**
        在 Anroid 中如果 Work Thread 更新 UI 會導致異常拋出，並不是因為 UI Thread 才能更新 UI，而是因為 ViewRootImpl 紀錄時，**就是紀錄 UI Thread**，**==非 ViewRootImpl 紀錄的 Thread 是不能更新 ViewRootImpl==**
    :::

* [**WindowManagerGlobal**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/WindowManagerGlobal.java)#getWindowSession 方法：是 Framework 與 Native Binder 通訊的入口，並且從這裡可以看到 WindowSession 服務
    > WindowSession 是一個隱密服務，ServiceManager 也不會記錄，必須透過 WMS 間接得到
    ```java=
    // WindowManagerGlobal.java

        private static IWindowManager sWindowManagerService;

        public static IWindowSession getWindowSession() {

            // 類鎖
            synchronized (WindowManagerGlobal.class) {
                if (sWindowSession == null) {
                    try {
                        InputMethodManager imm = InputMethodManager.getInstance();
                        // @ getWindowManagerService
                        // 獲取 WMS 服務，往下追
                        IWindowManager windowManager = getWindowManagerService();
                        // @ 與 WMS 建立一個 Session
                        sWindowSession = windowManager.openSession(...);
                        
                    } /* 省略 catch*/
                return sWindowSession;
            }
        }

        public static IWindowManager getWindowManagerService() {
            // 類鎖
            synchronized (WindowManagerGlobal.class) {
                // 檢查緩存
                if (sWindowManagerService == null) {
                    sWindowManagerService = IWindowManager.Stub.asInterface(
                            ServiceManager.getService("window"));
                    
                    ... 省略部份
                }
                return sWindowManagerService;
            }
        }

    
    // ------------------------------------------------------------------
    /** 
     * ServiceManager.java
     */ 
        public static IBinder getService(String name) {
            try {
                IBinder service = sCache.get(name);
                
                // 檢查本地是否有緩存
                if (service != null) {
                    return service;
                } else {
                    // 沒有緩存，則去 ServiceManager 進程取得 Service IBinder 對象
                    return getIServiceManager().getService(name);
                }
            } catch (RemoteException e) {
                Log.e(TAG, "error in getService", e);
            }
            return null;
        }
    ```
    1. ViewRootImpl 物件的建立，會儲存當前的 Thread
        > 這也就是我們 **如果使用 WorkThread 更新 UI 時為何會拋出異常**
    2. [**Binder 機制**](https://hackmd.io/yNGrVdN-RtelUqYQgn9avg#%E7%B5%90%E8%AB%96)：App 應用端透過 **asInterface 獲取遠端服務的代理，也就是獲取 WMS (IWindowManager) 服務的 ++代理++**
        > IWindowManager 是 `.aidl` 檔案，它的實做類（真正服務端）就是 WindowManagerService

        ```java=
        /** 
         * WindowManagerService.java
         */ 
        public class WindowManagerService extends IWindowManager.Stub
                implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {

            ... 省略

            @Override
            public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
                    IInputContext inputContext) {
                if (client == null) throw new IllegalArgumentException("null client");
                if (inputContext == null) throw new IllegalArgumentException("null inputContext");

                // 在 WMS 創建一個 Session 對象
                Session session = new Session(this, callback, client, inputContext);
                return session;
            }
        }
        ```
* 以上建立好 Session 後就可以 **使用 Session 來交換資訊**

    :::info
    * 當 ViewRootImpl & WindowManagerService 產生關連後，**還不能讓 View 顯示在螢幕上，==WMS 是管理 View 的 Z 軸==，++WMS 管理當前狀態下哪個 View 應該在最上層顯示++**
    
    * **其是 WMS 並不是管理 Window，而是==管理特定 Window 中的 View==**
    :::
    
    > ![](https://i.imgur.com/sbLEnOE.png)

:::info
* 從這邊也可以知道每次呼叫 WindowGlobalManager#addView 都會創建一個 ViewRootImpl，並對應創建一個 WindowSession
    > ![](https://i.imgur.com/dCtOJk6.png)

:::

### WindowSession 添加 ViewRootImpl
* 與 WMS 建立 Session 後會呼叫 ViewRootImpl 的 `setView` 方法：**該方法會向 WMS 發起顯示 `Dialog` or `Activity` 中的 ++DecorView 請求++**
    
    > 回到 WindowManagerGlobal 中的 addView 方法的最後一個步驟 "setView" (起初是 Dialog 調用)
    ```java=
    /**
     * WindowManagerGlobal.java
     */ 
     public void addView(View view, ViewGroup.LayoutParams params,
                Display display, Window parentWindow) { 
            ...

            ViewRootImpl root;
            View panelParentView = null;

            // mLock 普通對象鎖
            synchronized (mLock) {
                // Start watching for system property changes.
                ...

                // 建立 ViewRootImpl 對象 
                root = new ViewRootImpl(view.getContext(), display);
                // 設定布局參數
                view.setLayoutParams(wparams);

                // 以下都是 ArrayList
                mViews.add(view);			// View
                mRoots.add(root);			// ViewRootImpl
                mParams.add(wparams);		// WindowManager.LayoutParams 視窗參數
            }

            //最後執行此操作，因為它會觸發傳遞消息
            try {
                // @ 追蹤 ViewRootImpl#setView 方法
                root.setView(view, wparams, panelParentView);
            } /* 省略 catch */
    }


    // ---------------------------------------------------------------------
    /**
     * ViewRootImpl.java
     */ 
        final W mWindow;
        
        public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
                boolean useSfChoreographer) {
            
            ... 省略部份

            mWindow = new W(this);
        }

        public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
            synchronized (this) {

            // 由於過於複雜，這裡顯示關鍵函數
            requestLayout();
            try {
                    ...
                    // 向 WMS 發起請求
                    // @ 查看 addToDisplay
                    res = mWindowSession.addToDisplay(mWindow, 
                                                      mSeq, 
                                                      mWindowAttributes,
                                                        getHostVisibility(), 
                                                      mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
            } // 省略 catch、finally
        }

    ```
* WindowSession#addToDisplay 是添加對 Binder 傳送一個 [**IWindow**](https://cs.android.com/android/platform/superproject/+/master:out/soong/.intermediates/frameworks/base/framework-minus-apex-intdefs/android_common/xref33/srcjars.xref/android/view/IWindow.java) 對象，如果 WMS 需要調整該 View 的話，就會通過 IWindow 來通知該 view

    ```java=
    // IWindow.java

    public interface IWindow extends android.os.IInterface
    {
        @Override 
        public void executeCommand(java.lang.String command, java.lang.String parameters, android.os.ParcelFileDescriptor descriptor) throws android.os.RemoteException
        {
        }

        @Override 
        public void resized(android.window.ClientWindowFrames frames, boolean reportDraw, android.util.MergedConfiguration newMergedConfiguration, boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId) throws android.os.RemoteException
        {
        }

        ... 省略部份方法
    }
    ```
    
    :::info
    * 其實也就是把 View (管理者為 ViewRootImpl) 添加進 WindowSession
    :::

### [ViewRootImpl](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/ViewRootImpl.java) - setView 本地繪製
* ViewRootImpl#setView 的內容十分的多，所以我這先提出呼叫 `setView` 時較為關鍵的函數，分別是 
    1. **requestLayout(要求布局)**

    2. **checkThread(檢查是否是主線程更新)**
        :::warning
        這裡就會檢查是否是當初呼叫 ViewRootImpl 的 Thread
        :::
    
    3. **scheduleTraversals(準備測量、繪製)**
        > 之後會觸發 **performTraversals** 方法
    
    ```java=
    /**
     * ViewRootImpl.java
     * 
     * #setView 函數呼叫 requestLayout 函數
     */ 
        @Override
        public void requestLayout() {
            if (!mHandlingLayoutInLayoutRequest) 
                // 檢查線程是否切換
                checkThread();
                mLayoutRequested = true;
                // 安排繪製
                scheduleTraversals();
            }
        }

        void checkThread() {
            // @ 只有創建 ViewRootImpl 對象的 Thread 可以往下操作執行
            if (mThread != Thread.currentThread()) {
                throw new CalledFromWrongThreadException(
                        "Only the original thread that created a view hierarchy can touch its views.");
            }
        }

        void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;

                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

                // "1. " 注意第二個參數 mTraversalRunnable
                mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, 
                    mTraversalRunnable, 
                    null
                );
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }

        // 沒規定 Looper，預設當前的 Thread 的 Looper （Main Thread）
        final ViewRootHandler mHandler = new ViewRootHandler();
    ```
    * mTraversalRunnable：它是一個 **Runnable 任務**，，該任務執行 **doTraversal** 方法
    
        ```java=
        // ViewRootImpl.java

            final class TraversalRunnable implements Runnable {
                @Override
                public void run() {
                    doTraversal();
                }
            }

            final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

            ...

            void doTraversal() {
                if (mTraversalScheduled) {
                    mTraversalScheduled = false;
                    mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

                    if (mProfile) {
                        Debug.startMethodTracing("ViewAncestor");
                    }

                    performTraversals();

                    if (mProfile) {
                        Debug.stopMethodTracing();
                        mProfile = false;
                    }
                }
            }

            // performTraversals 函數也相當的攏長
            private void performTraversals() {
                ...
            }
        ```
    :::success
    * performTraversals 主要做以下 4 個任務

        1. 獲取 `Surface` 物件，用於圖形繪製
        2. 側兩整個檢視樹的各個 View 的大小，**performMeasure** 函數
            > 觸發 onMeasure
        3. 布局整個 View 樹，**performLayout** 函數
            > 觸發 onLayout 
        4. 繪製整個 View 樹，**performDraw** 函數
            > 觸發 onDraw 
    :::

### ViewRootImpl 取得 Canvas - onDraw 繪製
* ViewRootImpl#performTraversals 函數，其中第 4 個要點繪製 View 樹，Framwork 會獲取到圖形繪製表面 `Surface` (也就是 `Canvas` 對象)，View 就會繪製在 Canvas 上

    ```java=
    // ViewRootImpl.java 

        private void performDraw() {
            if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
                return;
            }

            final boolean fullRedrawNeeded = mFullRedrawNeeded;
            mFullRedrawNeeded = false;

            mIsDrawing = true;
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
            try {
                // 呼叫繪製函數
                draw(fullRedrawNeeded);
            } finally {
                mIsDrawing = false;
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
            ...
        }

        private void draw(boolean fullRedrawNeeded) {
            // 1. 繪製表面
            Surface surface = mSurface;
            if (!surface.isValid()) {
                return;
            }

            ...
            // 2. 繪圖表面更新
            if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
                // 使用 GPU 繪製，也就是硬體加速
                if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {

                    ...

                    if (updated) {
                        requestDrawWindow();
                    }
                    // 3. 硬體加速 ! 使用硬體渲染
                    mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
                } else {

                    // 4. 使用 CPU 繪製圖形
                    if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                        return;
                    }
                }
            }

            if (animating) {
                mFullRedrawNeeded = true;
                scheduleTraversals();
            }
        }
    ```
* 在 ViewRootImpl#performDraw 的過程中主要有分為：^1.^ CPU 繪製、^2.^ GPU 繪製，大部分狀況下我們都是使用 CPU 繪製

    :::success
    * Surface 中的可繪製區域就是 Canvas，Surface 就是一個巨大畫布，Canvas 就是該畫布要繪製的區塊
    :::
    ```java=
    // ViewRootImpl.java 

        private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
                boolean scalingRequired, Rect dirty) {

            // Draw with software renderer.
            final Canvas canvas;
            try {
                final int left = dirty.left;
                final int top = dirty.top;
                final int right = dirty.right;
                final int bottom = dirty.bottom;

                // 1. 獲取指定區域的 Cavans 物件
                canvas = mSurface.lockCanvas(dirty);

                ...

                // TODO: Do this in native
                canvas.setDensity(mDensity);
            
            } /* 省略 catch */

            try {
                ...

                try {
                    // 2. 從 DecorView 開始繪製，也就是整個 Window 的根檢視，這會引起整個 View 樹的重繪操作
                    mView.draw(canvas);

                    drawAccessibilityFocusedDrawableIfNeeded(canvas);
                } finally {
                    if (!attachInfo.mSetIgnoreDirtyState) {
                        // Only clear the flag if it was not set during the mView.draw() call
                        attachInfo.mIgnoreDirtyState = false;
                    }
                }
            } finally {
                try {
                    // 3. 釋放 Canvas 鎖，並通知 SurfaceFlinger 更新這塊區域
                    surface.unlockCanvasAndPost(canvas);
                }
                ...
            }
            return true;
        }
    ```

## 結論
* Dialog View 創建的概念圖
    
    > ![](https://i.imgur.com/SLHWNly.png)



## Appendix & FAQ

:::info
:::

###### tags: `Android Framework`
