我的
CSDN
博客同步發布:Window與WMS通信過程
轉載請注明出處:【huachao1001的簡書:http://www.lxweimin.com/users/0a7e42698e4b/latest_articles】
上一篇文章【理清Activity、View及Window之間關系】我們大致知道了Window
的繪制過程,但是比較籠統,本文主要介紹Window
對象與(后面縮寫為WMS
)之間是如何通信。毫無疑問,肯定是通過IPC
(Binder
機制),這點肯定都知道,但是我們要學習是的是,哪些類參與了IPC
調用過程。另外,本文沒有研究源碼,而是通過閱讀其他研究源碼的文章,然后總結出來,以更容易理解的方式展示。本文設計到的相關資料在文章最后一一列出。
1 Window添加的大致過程
Window
的添加過程需要通過WindowManager
的addView
來實現,WindowManager
是一個接口,真正的實現是WindowManagerImpl
。而WindowManagerImpl
全部是轉移給WindowManagerGlobal
來處理,WindowManagerImpl
這種工作模式是典型的橋接模式。WindowManagerImpl
內三大操作過程如下:
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
,其內部維護如下幾個列表:
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
所對應的View
- mRoots:存儲的是所有
Window
所對應的ViewRootImpl
- mParams:存儲的是所有
Window
所對應的布局參數- mDyingView:存儲了那些正在被刪除的
View
對象,或者說是那些已經調用了removeView
方法但是刪除操作還未完成的Window
對象。
addView
中通過如下方式將Window
的一系列對象添加到列表中:
root=new ViewRootImpl(view.getContext(),display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
可以看到,到目前為止,只是把相應的對象存放到ArrayList
列表中。后面還需要將View
給顯示出來。繪制View
需要通過ViewRootImpl
的setView
方法來實現。在setView
內部是通過requestLayout
來完成一部刷新請求的。
public void requestLayout(){
if(!mHandlingLayoutInlayoutRequest){
checkThread();
mLayoutRequested=true;
scheduleTraversals();
}
}
其中scheduleTraversals
是View
繪制的入口!
下面我們看看ViewRootImpl
是內部機制。
2 ViewRootImpl內部機制
ViewRootImpl
用于管理窗口的根View
,并和WMS
進行交互。ViewRootImpl
中有一個內部類: W
,以及另一個內部類:ViewRootHandler
。
W
繼承自IWindow.Stub
。是一個Binder
對象,用于接收WMS
的各種消息, 如按鍵消息, 觸摸消息等。
ViewRootHandler
,是Handler
的子類,W
會通過Looper
把消息傳遞給ViewRootHandler
。
ViewRootImpl
有一個W
類型的成員mWindow
,ViewRootImpl
在構造函數中創建一個W
的實例并賦值給mWindow
。
在ViewRootImpl
的setView
方法(此方法運行在UI
線程)中,會通過IPC
的方式跨進程向WMS
發起一個遠程調用,從而將DecorView
最終添加到Window
上,在這個過程中,ViewRootImpl
、DecorView
和WMS
會彼此向關聯.
另外,WMS
有時也需要向ViewRootImpl
發送遠程請求,比如,點擊事件是由用戶的觸摸行為所產生的,因此它必須要通過硬件來捕獲,跟硬件之間的交互自然是Android系統自己把握,Android系統將點擊事件交給WMS
來處理。WMS
通過遠程調用將事件發送給ViewRootImpl
,在ViewRootImpl
中,有一個方法,叫做dispatchInputEvent
,最終將事件傳遞給DecorView
。
3 Window與WMS之間的雙向通信
接下來,由WindowSession
來完成最后的Window
添加過程。mWindowSession
本身是一個IWindowSession
類型對象,通過內部代理類Proxy
訪問遠程Session
類(Binder
機制) 。在Session
內部通過WMS
來實現Window
的添加。如此一來Window
的添加請求就交給了WMS
去處理了,如下圖所示:
WindowManagerService
內部為每個應用保留一個單獨的Session
,如下圖所示:
前面我們說過,每個Window
對應一個ViewRootImpl
及一個View Tree
。也就是說,每個Activity
對應的Window
(一個或多個)內通過其內置的ViewRootImpl
完成向WMS
的請求過程。
一個應用中的所有Activity
共用一個Session
,一個Window
在WMS
內部對應一個WindowState
,WindowState
維護窗口的狀態以及根據適當的機制來調整窗口的狀態。
如果一個Activity
多個Window
,如對話框、Popup
類型、或者通過ViewManager
將View
直接加入WMS
,等等。在這些情況下,一個Activity
就會創建多個Window
,相應的WMS
中也會對應多個WindowState
,如下圖所示:
4 WMS控制窗口的顯示
以下內容來自老羅的的博客,后面附有資料鏈接。
WMS
服務大致按照以下方式來控制哪些窗口需要顯示的以及要顯在哪里:
每一個
Activity
窗口的大小都等于屏幕的大小,因此,只要對每一個Activity
窗口設置一個不同的Z
軸位置,然后就可以使得位于最上面的,即當前被激活的Activity
窗口,才是可見的。每一個子窗口的Z軸位置都比它的父窗口大,但是大小要比父窗口小,這時候
Activity
窗口及其所彈出的子窗口都可以同時顯示出來。對于非全屏
Activity
窗口來說,它會在屏幕的上方留出一塊區域,用來顯示狀態欄。這塊留出來的區域稱對于屏幕來說,稱為裝飾區(decoration
),而對于Activity
窗口來說,稱為內容邊襯區(Content Inset
)。輸入法窗口只有在需要的時候才會出現,它同樣是出現在屏幕的裝飾區或者說
Activity
窗口的內容邊襯區的。對于壁紙窗口,它出現需要壁紙的
Activity
窗口的下方,這時候要求Activity
窗口是半透明的,這樣就可以將它后面的壁紙窗口一同顯示出來。兩個
Activity
窗口在切換過程,實際上就是前一個窗口顯示退出動畫而后一個窗口顯示開始動畫的過程,而在動畫的顯示過程,窗口的大小會有一個變化的過程,這樣就導致前后兩個Activity
窗口的大小不再都等于屏幕的大小,因而它們就有可能同時都處于可見的狀態。事實上,Activity
窗口的切換過程是相當復雜的,因為即將要顯示的Activity
窗口可能還會被設置一個啟動窗口(Starting Window
)。一個被設置了啟動窗口的Activity
窗口要等到它的啟動窗口顯示了之后才可以顯示出來。
參考資料: