[學習筆記]Android開發藝術探索:理解Window和WindowManager

Window是一個抽象類,具體實現是 PhoneWindow 。不管是 Activity 、 Dialog 、 Toast 它們的視圖都是附加在Window上的,因此Window實際上是View的直接管理者。 WindowManager 是外界訪問Window的入口,通過WindowManager可以創建Window,而 Window的具體實現位於 WindowManagerService 中,WindowManager和 WindowManagerService的交互是一個IPC過程。

Window和WindowManager

通過WindowManager添加Window的過程:

mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
mFloatingButton = new Button(this);
mFloatingButton.setText("click me");
mLayoutParams = new WindowManager.LayoutParams(
       LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,
       PixelFormat.TRANSPARENT);
mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
       | LayoutParams.FLAG_NOT_FOCUSABLE
       | LayoutParams.FLAG_SHOW_WHEN_LOCKED;
mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;
mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
mLayoutParams.x = 100;
mLayoutParams.y = 300;
mFloatingButton.setOnTouchListener(this);
mWindowManager.addView(mFloatingButton, mLayoutParams);

WindowManager的flags和type這兩個屬性比較重要: Flags代表Window的屬性,控制Window的顯示特性

  1. FLAG_NOT_FOCUSABLE 在此模式下,Window不需要獲取焦點,也不需要接收各種輸 入事件,這個標記同時會啓用FLAG_NOT_TOUCH_MODAL,最終事件會直接傳遞給下層具有焦點的Window。
  2. FLAG_NOT_TOUCH_MODAL 在此模式下,系統將當前Window區域以外的點擊事件傳 遞給底層的Window,當前Window區域內的單擊事件則自己處理。一般需要開啓此標 記。
  3. FLAG_SHOW_WHEN_LOCKED 開啓此模式Window將顯示在鎖屏界面上。

type參數表示Window的類型。

  1. 應用Window

  2. 子Window 如Dialog

  3. 系統Window 如Toast和系統狀態欄

Window是分層的,每個Window對應一個z-ordered,層級大的會覆蓋在層級小的上面,和 HTM的z-index概念一樣。在三類Window中,應用Window的層級範圍是1~99,子Window的 層級範圍是10001999,系統Window的層級範圍是20002999,這些值對應 WindowManager.LayoutParams的type參數。一般系統Window選用 TYPE_SYSTEM_OVERLAY 或者 TYPE_SYSTEM_ERROR (同時需要權限 )。

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

WindowManager提供的功能很簡單,常用的只有三個方法:

  1. 添加View
  2. 更新View
  3. 刪除View

這個三個方法定義在 ViewManager 中,而WindowManager繼承了ViewManager。

public interface ViewManager
{
      /**
      * Assign the passed LayoutParams to the passed View and add the view to the windo
      w.
      * <p>Throws {@link android.view.WindowManager.BadTokenException} for certain prog
      ramming
      * errors, such as adding a second view to a window without removing the first vie
      w.
      * <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the win
      dow is on a
      * secondary {@link Display} and the specified display can't be found
      * (see {@link android.app.Presentation}).
      * @param view The view to be added to this window.
      * @param params The LayoutParams to assign to view.
      */
      public void addView(View view, ViewGroup.LayoutParams params);
      public void updateViewLayout(View view, ViewGroup.LayoutParams params);
      public void removeView(View view);
}

Window的內部機制

Window是一個抽象的概念,每一個Window都對應着一個View和一個ViewRootImpl, Window和View通過ViewRootImpl來建立聯繫。因此Window並不是實際存在的,它是以 View的形式存在的。所以WindowManager的三個方法都是針對View的,說明View纔是 Window存在的實體。在實際使用中無法直接訪問Window,必須通過WindowManager來訪問 Window。

Window的添加過程,刪除過程 ,更新過程都是通過WindowManager的真正實現類—— WindowManagerImpl 類的三大方法的源碼來對Window的內部機制進行分析。 具體點說就是,WindowManagerImpl並沒有直接實現Window的三大操作,而是全部交 給 WindowManagerGlobal 處理。然後在 WindowManagerGlobal 內部都是通過 ViewRootImpl 裏的 一個Binder對象 mWindowSession ( IWindowSession 類型)進行IPC調用 WindowManagerService 進行Window的三大操作。

Window的創建過程

View是Android中視圖的呈現方式,但是View不能單獨存在,必須附着在Window這個抽象的概念上面,因此有視圖的地方就有Window。這些視圖包括: Activity、Dialog、Toast、PopUpWindow等等。

  1. 在創建視圖並顯示出來時,首先是通過創建一個Window對象,然後通過 WindowManager對象的 addView(View view, ViewGroup.LayoutParams params); 方法將 contentView 添加到Window中,完成添加和顯示視圖這兩個過程。

  2. 在關閉視圖時,通過WindowManager來移除 DecorView, mWindowManager.removeViewImmediate( view); 。

  3. Toast比較特殊,具有定時取消功能,所以系統採用了Handler,內部有兩類IPC過程:

    i. Toast訪問 NotificationManagerService

    ii. NotificationManagerService 回調Toast裏的 TN 接口

顯示和隱藏Toast都通過NotificationManagerService(NMS)來實現,而NMS運行在系統進程中,所以只能通過IPC來進行顯示/隱藏Toast。而TN是一個Binder類,在Toast和NMS進行 IPC的過程中,當NMS處理Toast的顯示/隱藏請求時會跨進程回調TN中的方法,這時由於TN 理解Window和Window Manager 運行再Binder線程池中,所以需要通過Handler將其切換到當前線程(即發起Toast請求所在的線程),然後通過WindowManager的 addView/removewView 方法真正完成顯示和隱藏Toast。 本章節對源碼的分析側重的是整體流程,避免深入代碼邏輯無法自拔的情形。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章