我的
CSDN
博客同步發(fā)布:Window與WMS通信過程
轉(zhuǎn)載請注明出處:【huachao1001的簡書:http://www.reibang.com/users/0a7e42698e4b/latest_articles】
上一篇文章【理清Activity藻烤、View及Window之間關(guān)系】我們大致知道了Window
的繪制過程或辖,但是比較籠統(tǒng),本文主要介紹Window
對象與(后面縮寫為WMS
)之間是如何通信怀伦。毫無疑問帅容,肯定是通過IPC
(Binder
機(jī)制)装畅,這點(diǎn)肯定都知道晶衷,但是我們要學(xué)習(xí)是的是,哪些類參與了IPC
調(diào)用過程抄邀。另外耘眨,本文沒有研究源碼,而是通過閱讀其他研究源碼的文章境肾,然后總結(jié)出來剔难,以更容易理解的方式展示。本文設(shè)計(jì)到的相關(guān)資料在文章最后一一列出奥喻。
1 Window添加的大致過程
Window
的添加過程需要通過WindowManager
的addView
來實(shí)現(xiàn)偶宫,WindowManager
是一個接口,真正的實(shí)現(xiàn)是WindowManagerImpl
衫嵌。而WindowManagerImpl
全部是轉(zhuǎn)移給WindowManagerGlobal
來處理读宙,WindowManagerImpl
這種工作模式是典型的橋接模式彻秆。WindowManagerImpl
內(nèi)三大操作過程如下:
public void addView(View view,ViewGroup.LayoutParams params){
mGlobal.addView(view,params,mDisplay,mParantWindow);
}
public void updateViewLayout(View view,ViewGroup.LayoutParams params){
mGlobal.updateViewLayout(view,params);
}
public void removeView(View view){
mGlobal.removeView(view,false);
}
從WindowManagerGlobal
名稱可以看出楔绞,它是一個全局的WindowManager
,其內(nèi)部維護(hù)如下幾個列表:
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 final ArraySet<View> mDyingViews = new ArraySet<View>();
其中:
- mViews:存儲所有
Window
所對應(yīng)的View
- mRoots:存儲的是所有
Window
所對應(yīng)的ViewRootImpl
- mParams:存儲的是所有
Window
所對應(yīng)的布局參數(shù)- mDyingView:存儲了那些正在被刪除的
View
對象唇兑,或者說是那些已經(jīng)調(diào)用了removeView
方法但是刪除操作還未完成的Window
對象酒朵。
addView
中通過如下方式將Window
的一系列對象添加到列表中:
root=new ViewRootImpl(view.getContext(),display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
可以看到,到目前為止扎附,只是把相應(yīng)的對象存放到ArrayList
列表中蔫耽。后面還需要將View
給顯示出來。繪制View
需要通過ViewRootImpl
的setView
方法來實(shí)現(xiàn)。在setView
內(nèi)部是通過requestLayout
來完成一部刷新請求的匙铡。
public void requestLayout(){
if(!mHandlingLayoutInlayoutRequest){
checkThread();
mLayoutRequested=true;
scheduleTraversals();
}
}
其中scheduleTraversals
是View
繪制的入口图甜!
下面我們看看ViewRootImpl
是內(nèi)部機(jī)制。
2 ViewRootImpl內(nèi)部機(jī)制
ViewRootImpl
用于管理窗口的根View
鳖眼,并和WMS
進(jìn)行交互黑毅。ViewRootImpl
中有一個內(nèi)部類: W
,以及另一個內(nèi)部類:ViewRootHandler
钦讳。
W
繼承自IWindow.Stub
矿瘦。是一個Binder
對象,用于接收WMS
的各種消息愿卒, 如按鍵消息缚去, 觸摸消息等。
ViewRootHandler
琼开,是Handler
的子類易结,W
會通過Looper
把消息傳遞給ViewRootHandler
。
ViewRootImpl
有一個W
類型的成員mWindow
稠通,ViewRootImpl
在構(gòu)造函數(shù)中創(chuàng)建一個W
的實(shí)例并賦值給mWindow
衬衬。
在ViewRootImpl
的setView
方法(此方法運(yùn)行在UI
線程)中,會通過IPC
的方式跨進(jìn)程向WMS
發(fā)起一個遠(yuǎn)程調(diào)用改橘,從而將DecorView
最終添加到Window
上滋尉,在這個過程中,ViewRootImpl
飞主、DecorView
和WMS
會彼此向關(guān)聯(lián).
另外狮惜,WMS
有時也需要向ViewRootImpl
發(fā)送遠(yuǎn)程請求,比如碌识,點(diǎn)擊事件是由用戶的觸摸行為所產(chǎn)生的碾篡,因此它必須要通過硬件來捕獲,跟硬件之間的交互自然是Android系統(tǒng)自己把握筏餐,Android系統(tǒng)將點(diǎn)擊事件交給WMS
來處理开泽。WMS
通過遠(yuǎn)程調(diào)用將事件發(fā)送給ViewRootImpl
,在ViewRootImpl
中魁瞪,有一個方法穆律,叫做dispatchInputEvent
,最終將事件傳遞給DecorView
导俘。
3 Window與WMS之間的雙向通信
接下來峦耘,由WindowSession
來完成最后的Window
添加過程。mWindowSession
本身是一個IWindowSession
類型對象旅薄,通過內(nèi)部代理類Proxy
訪問遠(yuǎn)程Session
類(Binder
機(jī)制) 辅髓。在Session
內(nèi)部通過WMS
來實(shí)現(xiàn)Window
的添加。如此一來Window
的添加請求就交給了WMS
去處理了,如下圖所示:
WindowManagerService
內(nèi)部為每個應(yīng)用保留一個單獨(dú)的Session
洛口,如下圖所示:
前面我們說過矫付,每個Window
對應(yīng)一個ViewRootImpl
及一個View Tree
。也就是說第焰,每個Activity
對應(yīng)的Window
(一個或多個)內(nèi)通過其內(nèi)置的ViewRootImpl
完成向WMS
的請求過程技即。
一個應(yīng)用中的所有Activity
共用一個Session
,一個Window
在WMS
內(nèi)部對應(yīng)一個WindowState
樟遣,WindowState
維護(hù)窗口的狀態(tài)以及根據(jù)適當(dāng)?shù)臋C(jī)制來調(diào)整窗口的狀態(tài)而叼。
如果一個Activity
多個Window
,如對話框豹悬、Popup
類型葵陵、或者通過ViewManager
將View
直接加入WMS
,等等瞻佛。在這些情況下脱篙,一個Activity
就會創(chuàng)建多個Window
,相應(yīng)的WMS
中也會對應(yīng)多個WindowState
伤柄,如下圖所示:
4 WMS控制窗口的顯示
以下內(nèi)容來自老羅的的博客绊困,后面附有資料鏈接。
WMS
服務(wù)大致按照以下方式來控制哪些窗口需要顯示的以及要顯在哪里:
每一個
Activity
窗口的大小都等于屏幕的大小适刀,因此秤朗,只要對每一個Activity
窗口設(shè)置一個不同的Z
軸位置,然后就可以使得位于最上面的笔喉,即當(dāng)前被激活的Activity
窗口取视,才是可見的。每一個子窗口的Z軸位置都比它的父窗口大常挚,但是大小要比父窗口小作谭,這時候
Activity
窗口及其所彈出的子窗口都可以同時顯示出來。對于非全屏
Activity
窗口來說奄毡,它會在屏幕的上方留出一塊區(qū)域折欠,用來顯示狀態(tài)欄。這塊留出來的區(qū)域稱對于屏幕來說吼过,稱為裝飾區(qū)(decoration
)锐秦,而對于Activity
窗口來說,稱為內(nèi)容邊襯區(qū)(Content Inset
)那先。輸入法窗口只有在需要的時候才會出現(xiàn)农猬,它同樣是出現(xiàn)在屏幕的裝飾區(qū)或者說
Activity
窗口的內(nèi)容邊襯區(qū)的赡艰。對于壁紙窗口售淡,它出現(xiàn)需要壁紙的
Activity
窗口的下方,這時候要求Activity
窗口是半透明的,這樣就可以將它后面的壁紙窗口一同顯示出來揖闸。兩個
Activity
窗口在切換過程揍堕,實(shí)際上就是前一個窗口顯示退出動畫而后一個窗口顯示開始動畫的過程,而在動畫的顯示過程汤纸,窗口的大小會有一個變化的過程衩茸,這樣就導(dǎo)致前后兩個Activity
窗口的大小不再都等于屏幕的大小,因而它們就有可能同時都處于可見的狀態(tài)贮泞。事實(shí)上楞慈,Activity
窗口的切換過程是相當(dāng)復(fù)雜的,因?yàn)榧磳⒁@示的Activity
窗口可能還會被設(shè)置一個啟動窗口(Starting Window
)啃擦。一個被設(shè)置了啟動窗口的Activity
窗口要等到它的啟動窗口顯示了之后才可以顯示出來囊蓝。
參考資料: