Window 的新增過程
Window(或者說View) 是怎麼新增到 Android 系統中然後展示給使用者的?讓我們來探索一下 Window 的新增過程。
Window 新增過程的入口方法
要探索新增的過程,必須先在原始碼中找到新增 Window 的入口方法。
Window 的新增需要通過 WindowManager 的 addView
方法實現,但 WindowManager 是個介面,它的真正實現類是 WindowManagerImpl 類,但 WindowManagerImpl 也並沒有直接實現對 Window 的新增、刪除、更新操作,而是通過橋接模式將所有操作委託給 WindowManagerGlobal 去實現。最終會呼叫 WindowManagerGlobal 類的 addView
方法真正開啟 View 的新增過程。
所有,Window 新增過程的真正入口方法實際上是 WindowManagerGlobal 類的 addView
方法。
Window 新增過程的主要流程
WindowManagerGlobal 的 addView
方法主要乾了三件事:
-
檢查引數 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"); } ... } 複製程式碼
-
建立 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); ... } 複製程式碼
-
通過 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
方法最終會呼叫performTraversals
方法,我們知道performTraversals
是 View 執行繪製過程的入口方法,該方法會經過測量、佈局、繪製這三個過程把 View 繪製出來。 -
View 繪製出來以後是怎麼通過IPC呼叫的方式新增到 Window 中的呢?
我們知道,WindowManager 是外界訪問 Window 的入口,所以最終 WindowManager 會通過 IPC 的方式呼叫 WindowManagerService 的
addWindow
方法,這樣一來, Window 的新增請求就交給了 WindowManagerService 來處理了,然後 WindowManagerService 會經過一系列的操作將 View 新增到 Window 中並展示出來。作為應用層開發者來說,瞭解到這個程度個人覺得就可以了,沒必要去深究 WindowManagerService 的實現細節,至於 WindowManagerService 是如何處理 Window 的新增請求的,感興趣的讀者可以去檢視原始碼。
參考書籍:《Android 開發藝術探索》
-