Android的Window和WindowManager

       在日常開發中直接接觸Window的機會並不多,但是比如我們需要在界面顯示一個類似懸浮窗的東西時,這種效果就需要用到Window來實現。Window實際上是一個抽象類,他的具體實現是PhoneWindow,位於WindowManagerService中。創建Window是通過WindowManager實現的,WindowManager實際上是外界訪問Window的入口。而WindowManagerService和WindowManager的交互實際上是一個IPC(跨進程通信Inter-Process-Communication)過程。Android中不管Activity、Toast、Dialog等所有視圖都是通過Window呈現的,也就是附加在Window上。所以Window其實是View的直接管理者。那前面博文講的Activity設置視圖的方法setContentView在底層其實也是通過Window來完成的。Window的功能更多是底層的支持,所以開發並不常用。

1.   Window和WindowManager

      要明白Window的工作機制,不如先從如何使用Windowmanager添加一個Window說起。

      Eg:Button  button  =  new Button (this);

              LayoutParams  layoutParams  =  new  WindowManager.LayoutParames(……);

              layoutParames.flags =

              LayoutParames.FLAG_NOT_TOUCH_MODAL|LayoutParames.FLAG_NOT_FOUCESABLE|                                                              LayoutParames.FLAG_SHOW_WHEN_LOCKED;

              layoutParames.gravity = Gravity.LEFT|Gravity.TOP;

              layoutParames.x = 100;

               layoutParames.y = 300;

              mWindowManager.addView ( button , layoutParams );

       上述代碼使把一個Buton添加到屏幕座標未(100,300)的位置上。其中WindowManager.LayoutParames的type和flags兩個參數比較重要。Flags控制Window的顯示特性。type參數表示Window的類型,Window一共三種類型:應用Window,子Window和系統Window。應用Window對應一個Activity,子Window只能附屬在特定的父Window中,比如Dialog就是一個子Window。而Toast就是一個系統Window。同時Window是分層的,層級大區的覆蓋在層級小的Window上面,所以可見系統Window的層級大於子Window,子Window的層級大於應用Window。這些層級對應的就是上面提到的type參數。如果type使用系統層級,一定要記得在AndroidManifest.xml中聲明權限。

<uses-permissionandroid:name= “android.permission.SYSTEM_ALERT_WINDOW”/>.

        WindowManager提供的方法很簡單,常用的也就三個:添加View,刪除View和更新View。這三個方法定義在ViewManager中,WindowManager繼承了ViewManager。

2.  Window的內部機制

        準確來講Window並不是實際存在的,他依託於View的形式存在,View纔是Window存在的實體,兩者通過ViewRootImpl建立聯繫。  View的繪製過程由ViewRootImpl的setView方法完成的.實際使用中使無法直接訪問Window的,對Window的訪問必須是通過WindowManager才能完成

        Window的添加過程:

        Window的添加通過WindowManager的addView實現。WindowManager實際是一個接口,他的實現是WindowManagerImpl,WindowManagerImpl又將Window的三大操作交給了WindowManagerGlobal。WindowManagerGlobal中維護的列表中有幾個比較重要的:

mView:所有Window對應的View;

mParams:所有Window所對應的佈局參數;

mDyingViews:正在被刪除的View對象;

      Window的刪除過程:

     和添加過程一樣,底層都是先通過WindowManagerImpl後,在經由WindowManagerGlobal來實現的。在WindowManager中提供了removeView和removeViewImmediate兩個刪除接口,分別表示異步刪除和同步刪除。使用後者可能會發生意外的錯誤,一般不需要使用。

      Window的更新過程:

      通過WindowManagerGlobal中的updateViewLayout完成。該方法做的比較簡單,先是用新的LayoutParams替換掉ViewRootImpl中舊的LaouyParams,在ViewRootImpl中通過scheduleTraversals對View重新佈局。最終是由WindowManagerervice的relayoutWindow()來具體實現,所以可見又是一個IPC過程。

3.  Window的創建過程

       View雖然使Android中視圖的呈現方式,但又不能獨立存在,必須依附於Window這個抽象的概念上面。所以有視圖的地方就一定有Window。Android中的Activity,Toast,Dialog,Menu其實都對應着一個Window。

       先簡述一下Activity中Window的創建過程:Activity啓動過程很複雜,但最終會由ActivityThread中的performLaunchActivity()來完成啓動過程,這個方法中會啓用類加載器創建Activity的實例對象,並調用attach方法爲Activity關聯運行過程中將依賴的一系列上下文環境變量。attach方法中系統會創建其所屬的Window並設置回調接口,Window對象的創建由PolicyManager的makeNewWindow實現,這是一個工廠方法。

        Window mWindow = PolicyManager.makeNewWindow(this);

        那再看看Activity的視圖使怎麼依附於Window上的。Activity中setContentView的源碼如下:

 public void setContentView(intlayoutResID){

       getWindow().setContentView(layoutResID);

       initWindowDecorActionBar();

}

       所以可以看出Activity將視圖的具體實現交給了Window,Window的具體實現是PhoneWindow。所以視圖的創建是在PhoneWinow中的setContentView()中完成的。DecorView是Activity中的頂級View,首先會創建一個DecorView,不過是一個空的FrameLayout,然後PhoneWindow加載佈局文件到這個FrameLayout。因爲Activity實現了Window的Callback接口,所以這時就回到Activity的onContentChanged方法通知Activity視圖已經發生變化。經過上面三個步驟,Activity的DecoeView已經被創建並初始化完畢。前面已經說了Activity的attach方法中Window已經被創建了,但DecorView還沒被添加到Window中,所以無法接受外界輸入。在ActivityThread的handleResumeActivity方法中,首先調用Activity的Resume方法,接着調用Activity的makeVisible(),在這個方法中真正完成了DecorView的添加和顯示的過程。這是Activity的視圖才能被用戶看到。

        Toast的Window相對複雜一點,因爲他是系統Window,顯然涉及到IPC。Toast涉及兩類IPC過程:第一類是NotificationManagerService(NMS),第二類是NotificationManagerService回調Toast的TN接口,這個接口運行在Binder線程池中,他提供了show和cancle兩個方法將Toast的視圖添加到Window中,完成Toast的顯示和隱藏。NMS中的enqueueToast方法首先將Toast請求封裝成ToastRecord對象並添加到隊列mToastQueue中。之後NMS通過內置方法,由ToastRecord的callback回調完成。這個回調就是前面提到的TN對象的遠程Binder,爲了切換到Toast請求所在的線程,在他們的內部使用了Handler。同理,Dialog的Window的創建過程和Activity是類似的。介紹到這裏很清楚了,就是任何View都是附屬在一個Window上面的。

發佈了29 篇原創文章 · 獲贊 4 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章