Window 的添加過程

Window 的添加過程

Window(或者說View) 是怎麼添加到 Android 系統中然後展示給用戶的?讓我們來探索一下 Window 的添加過程。

Window 添加過程的入口方法

要探索添加的過程,必須先在源代碼中找到添加 Window 的入口方法。

Window 的添加需要通過 WindowManager 的 addView 方法實現,但 WindowManager 是個接口,它的真正實現類是 WindowManagerImpl 類,但 WindowManagerImpl 也並沒有直接實現對 Window 的添加、刪除、更新操作,而是通過橋接模式將所有操作委託給 WindowManagerGlobal 去實現。最終會調用 WindowManagerGlobal 類的 addView 方法真正開啓 View 的添加過程。

所有,Window 添加過程的真正入口方法實際上是 WindowManagerGlobal 類的 addView 方法。

Window 添加過程的主要流程

WindowManagerGlobal 的 addView 方法主要分爲三大步:

  1. 檢查參數 params 是否是 WindowManager.LayoutParams,如果不是說明參數不合法,則會拋出異常。

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        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)) { // 檢查 params 參數是否合法
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }
        ...
    }
  2. 創建 ViewRootImpl,並將 View 添加到列表中。

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
            ...
            root = new ViewRootImpl(view.getContext(), display); // 創建 ViewRootImpl
            view.setLayoutParams(wparams);
            mViews.add(view); // 將View添加到mView列表中,mView 存儲的是所有Window對應的View
            mRoots.add(root);
            mParams.add(wparams);
            ...
    }
  3. 通過 ViewRootImpl 的 setView 方法來添加更新界面並通過 IPC 的方式調用 WindowManagerService 的 addWindow 方法完成 Window 的添加過程。

    public void addView(View view, ViewGroup.LayoutParams params,
    Display display, Window parentWindow) {
            ...
            // do this last because it fires off messages to start doing things
            try { 
                root.setView(view, wparams, panelParentView); // ViewRootImpl的setView 方法
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                synchronized (mLock) {
                    final int index = findViewLocked(view, false);
                    if (index >= 0) {
                        removeViewLocked(index, true);
                    }
                }
                throw e;
            }
    }
    • ViewRootImpl 的setView 方法是如何實現界面的更新的呢?

      setView 方法中會調用 requestLayout() 方法去完成異步刷新請求:

      @SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
      public final class ViewRootImpl implements ViewParent,
              View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
          private static final String TAG = "ViewRootImpl";
          ...
          // Schedule the first layout -before- adding to the window
          // manager, to make sure we do the relayout before receiving
          // any other events from the system.
          requestLayout();           
      } 

      我們再查看 requestLayout 方法的源碼,看它幹了什麼:

      @Override
      public void requestLayout() {
          if (!mHandlingLayoutInLayoutRequest) {
               checkThread();
               mLayoutRequested = true;
               scheduleTraversals(); // scheduleTraversals 方法是View繪製的入口
          }
      }

      可以看到,是調用了 scheduleTraversals 方法進行繪製,我們知道 scheduleTraversals 是 View 執行繪製過程的入口方法,該方法會經過測量、佈局、繪製這三個過程把 View 繪製出來。

    • View 繪製出來以後是怎麼通過IPC調用的方式添加到 Window 中的呢?

      我們知道,WindowManager 是外界訪問 Window 的入口,所以最終 WindowManager 會通過 IPC 的方式調用 WindowManagerService 的 addWindow 方法,這樣一來, Window 的添加請求就交給了 WindowManagerService 來處理了,然後 WindowManagerService 會經過一系列的操作將 View 添加到 Window 中並展示出來。

      作爲應用層開發者來說,瞭解到這個程度就差不多了,沒必要去深究 WindowManagerService 的實現細節,至於 WindowManagerService 是如何處理 Window 的添加請求的,感興趣的讀者可以去查看源碼。

參考書籍:

《Android 開發藝術探索》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章