ViewRootImpl是一個及其重要的類,主要作用如下:
(1)將DecorView傳遞給WindowManagerSerive。
(2)完成View的繪制過程,包括measure、layout、draw過程。
(3)向DecorView分發(fā)收到的用戶發(fā)起的event事件,如按鍵,觸屏等事件。
此外,ViewRootImpl中還包含了兩個需要重點關(guān)注的內(nèi)部類:
(1)final class ViewRootHandler extends Handler
用于向DecorView分發(fā)事件
(2)static class W extends IWindow.Stub
W是ViewRootImpl的一個嵌入類,也是一個Binder服務(wù)。通過mWindowsession.addToDisplay函數(shù)傳入WMS,用來在WMS中通過Binder回調(diào)。
/** * The top of a view hierarchy, implementing the needed PRotocol between View * and the WindowManager. This is for the most part an internal implementation * detail of {@link WindowManagerGlobal}. * * {@hide} */ 通過這一段注釋,我們知道,ViewRootImpl他是View樹的樹根,但它卻又不是View。ViewRootImpl的創(chuàng)建在ActivityThread中,調(diào)用棧如下: ActivityThread#handleResumeActivity()->WindowManagerImpl#addView()->WindowManagerGlobal#addView()->ViewRootImpl(view.getContext(),Display)。 1.1 構(gòu)造函數(shù)選取ViewRootImpl構(gòu)造函數(shù)的一部分源碼如下:mWindowSession = WindowManagerGlobal.getWindowSession(); mWindow = new W(this); mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this); 第一句mWindowSession賦值為WindowManagerGlobal.getWindowSession(),其返回值為一個Session對象,Session的實例化在WMS進程中進行的,ViewRootImpl中有一個Session的代理對象,所以可以通過Session主要調(diào)用WMS中一些方法。 第二、三句中的W繼承于IWindow.Stub,后者繼承于Binder又實現(xiàn)了IWindow接口,因此這個W是可以ipC的。第三句中將mWindow賦值給了View.AttachInfo中的mWindow對象,將mWindowSession賦值給了mSession變量。1.2 ViewRootImpl#setViewpublic void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; //略 requestLayout(); //略 try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel); } //略 } } } 首先,在setView內(nèi)部會通過requestLayout來完成異步刷新請求,requestLayout最終會調(diào)用performTraversals方法來完成View的繪制。 接著,會通過WindowSession最終來完成Window的添加過程。上面的代碼中,mWindowSession類型是IWindowSession,它是一個Binder對象,真正的實現(xiàn)類是Session,也就是說這其實是一次IPC過程,遠(yuǎn)程調(diào)用了Session中的addToDisPlay方法。 由此可知,接下去Window的添加請求就交給WindowManagerService去處理了。addView大概一個過程如下:WindowManager——>WindowManagerGobal——>ViewRootImpl——>Session——>WindowManagerService2、完成View的繪制過程
整個View樹的繪圖流程是在ViewRootImpl類的performTraversals()方法中進行的,該函數(shù)做的執(zhí)行過程主要是根據(jù)之前設(shè)置的狀態(tài),判斷是否重新計算視圖大小(measure)、是否重新放置視圖的位置(layout)、以及是否重繪 (draw),其核心也就是通過判斷來選擇順序執(zhí)行這三個方法中的哪個,如下:private void performTraversals() { ...... //最外層的根視圖的widthMeasureSpec和heightMeasureSpec由來 //lp.width和lp.height在創(chuàng)建ViewGroup實例時等于MATCH_PARENT int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); ...... mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); ...... mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight()); ...... mView.draw(canvas); ...... } performTraversals方法會經(jīng)過measure、layout和draw三個過程才能將一個View繪制出來,所以View的繪制是ViewRootImpl完成的。 另外當(dāng)手動調(diào)用invalidate,postInvalidate,requestInvalidate也會最終調(diào)用performTraversals,來重新繪制View。其中requestLayout()方法會調(diào)用measure過程和layout過程,不會調(diào)用draw過程,也不會重新繪制任何View包括該調(diào)用者本身。3、向DecorView分發(fā)事件
這里的事件不僅僅包括MotionEvent,還有KeyEvent。我們知道View的時間分發(fā)順序為Activity——>Window——>View,那么Activity的事件來源在哪里呢?這是個需要思考的問題,答案和ViewRootImpl有很大的關(guān)系。 首先,事件的根本來源來自于硬件,經(jīng)過native層,然后會經(jīng)過InputEventReceiver接受事件,然后交給ViewRootImpl,將事件傳遞給DecorView,DecorView再交給PhoneWindow,PhoneWindow再交給Activity。這樣看來,整個體系的事件分發(fā)順序為:3.1 ViewRootImpl#dispatchInputEvent
public void dispatchInputEvent(InputEvent event, InputEventReceiver receiver) { SomeArgs args = SomeArgs.obtain(); args.arg1 = event; args.arg2 = receiver; Message msg = mHandler.obtainMessage(MSG_DISPATCH_INPUT_EVENT, args); msg.setAsynchronous(true); mHandler.sendMessage(msg); } InputEvent輸入事件,它有2個子類:KeyEvent和MotionEvent,其中KeyEvent表示鍵盤事件,而MotionEvent表示點擊事件,這里InputEventReceiver譯為輸入事件接收者,顧名思義,就是用于接收輸入事件,然后交給ViewRootImpl的dispatchInputEvent方法去分發(fā)處理。可以看到mHandler將邏輯切換到UI線程,代碼如下。final ViewRootHandler mHandler = new ViewRootHandler(); final class ViewRootHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { ........ { SomeArgs args = (SomeArgs)msg.obj; InputEvent event = (InputEvent)args.arg1; InputEventReceiver receiver = (InputEventReceiver)args.arg2; enqueueInputEvent(event, receiver, 0, true); args.recycle(); } break; ................. } (未完待續(xù))
新聞熱點
疑難解答