多數情況我們是和Activity和View打交道,在之前學習中也都接觸過,本篇來深入學習和它們有緊密聯絡的Window,主要內容:
- Window&WindowManager
- Window的內部機制(新增、刪除、更新)
- Window的建立過程(Activity、Dialog、Toast)
1.Window&WindowManager
a.Window&PhoneWindow: Window是一個抽象類,它定義了頂級窗體樣式和行為。其唯一的實現類是PhoneWindow。
推薦閱讀:Window,PhoneWindow,DecorView,setContentView原始碼理解
b.Window&View: 每個Window都對應一個View和一個ViewRootImpl,Window和View通過ViewRootImpl來建立聯絡。Window並不可見,它實際以View的形式存在,它是View的直接管理者。
c.Window&WindowManager: 實際使用中無法訪問Window,對Window的訪問必須通過WindowManager,對Window的操作通過它完成。
- 例如:通過WindowManager新增Window
//將一個Button新增到螢幕為(100,300)的位置
mFloatingButton = new Button(this);
mFloatingButton.setText("test button");
mLayoutParams = new WindowManager.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,PixelFormat.TRANSPARENT);//第三個引數代表flags,第四個引數代表type
mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
| LayoutParams.FLAG_NOT_FOCUSABLE
| LayoutParams.FLAG_SHOW_WHEN_LOCKED;//配置flags
mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;//配置type
mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;//配置gravity
mLayoutParams.x = 100;//相對於gravity
mLayoutParams.y = 300;//相對於gravity
mFloatingButton.setOnTouchListener(this);
mWindowManager.addView(mFloatingButton, mLayoutParams);
複製程式碼
這裡依次介紹WindowManager的三個重要引數:
- flags:表示Window的屬性。主要的可選值含義:
- FLAG_NOT_FOCUSABLE:表示Window不需要獲取焦點,也不需要接收各種輸入事件,此標記會同時啟動FLAG_NOT_TOUCH_MODEL,最終事件會傳遞給下層的具有焦點的Window。
- FLAG_NOT_TOUCH_MODAL:表示系統會將當前Window區域以外的單擊事件傳遞給底層的Window,而區域以內的單擊事件則自己處理。一般都需要開啟此標記,否則其他Window將無法收到單擊事件。
- FLAG_SHOW_WHEN_LOCKED:表示Window可顯示在鎖屏介面。
- type:表示Window的型別。Window有三種型別:
- 應用Window:對應一個Activity。
- 子Window:不能單獨存在,需附屬特定的父Window。如Dialog。
- 系統Window: 需申明許可權才能建立。如Toast。
- Window是分層的,見下表。
- 層級大的會覆蓋在層級小的Window上面。
- 對應WindowManager.LayoutParams的type引數。
- gravity:表示Window的位置。
- 預設是螢幕中間。
- x、y值相對於gravity。
d.WindowManager&WindowManagerService: Window的具體實現位於WindowManagerService中。WindowManager和WindowManagerService的互動是一個IPC(跨程式通訊)過程。
2.Window的內部機制
- WindowManager對Window主要有三大操作:新增、更新和刪除。這三個方法主要是定義在ViewManager介面中:
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);//新增過程
public void updateViewLayout(View view, ViewGroup.LayoutParams params);//更新過程
public void removeView(View view);//刪除過程
}
複製程式碼
- WindowManager也是一個介面,它繼承了ViewManager介面:
public interface WindowManager extends ViewManager {}
複製程式碼
- WindowManager的具體實現類是WindowManagerImpl :
public final class WindowManagerImpl implements WindowManager{
@Override
public void addView(View view, ViewGroup.LayoutParams params){
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
@Override
public void updateViewLayout(View view, ViewGroup.LayoutParams params){
mGlobal.updateViewLayout(view, params);
}
@Override
public void removeView(View view){
mGlobal.removeView(view, false);
}
}
複製程式碼
- 由以上程式碼可見,WindowManagerImpl並沒有直接實現Window的三大操作,而是交給了WindowManagerGlobal。WindowManagerGlobal以單例模式向外提供自己的例項:
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
複製程式碼
一幅圖說明這幾個類的關係:
因此,通過WindowManagerGlobal的addView()、updateViewLayout()、removeView()實現WindowManager對Window的新增、刪除和修改。
下面分別來看WindowManagerGlobal對Window操作的大致過程:
a.Window的新增過程:
b.Window的刪除過程
c.Window的更新過程
不難發現, 以上驗證了之前的總結:
- Windows的三大操作最終都會通過一個IPC過程移交給WindowManagerService。
- Window和View通過ViewRootImpl來聯絡,ViewRootImpl可控制View的測量、佈局和重繪。
推薦閱讀:原始碼剖析之------Window的內部實現機制、我眼中的Window建立/新增/刪除/更新過程
3.Window的建立過程
由於View必須依附Window才能呈現出來,因此有View的地方必有Window。在Android中可以提供View的地方有Activity、Dialog和Toast,下面分別來看以上三種Window的大致建立過程:
a.Activity的Window建立過程
推薦閱讀:Activity的Window建立過程分析(原始碼)
b.Dialog的Window建立過程
Step1:建立WindowDialog。和Activity類似,同樣是通過PolicyManager.makeNewWindow() 來實現。
Step2:初始化DecorView並將Dialog的檢視新增到DecorView中去。和Activity類似,同樣是通過Window.setContentView() 來實現。
Step3:將DecorView新增到Window中顯示。和Activity一樣,都是在自身要出現在前臺時才會將新增Window。
- Dialog.show() 方法:完成DecorView的顯示。
- WindowManager.remoteViewImmediate() 方法:當Dialog被dismiss時移除DecorView。
c.Toast的Window建立過程
①Toast的內部的檢視由兩種方式指定:
- 系統預設的樣式;
- 通過setView()指定一個自定義View。
②Toast具有定時取消功能,故系統採用Handler做定時處理。
③在Toast內部有兩類IPC過程:
- Toast訪問NotificationManagerService(NMS);
- NotificationManagerService回撥Toast裡的TN介面。
④Toast提供方法show() 和cancel() 分別用於顯示和隱藏Toast。
- Toast的顯示和隱藏都需要通過NMS來實現,由於NMS執行在系統程式中,故需通過遠端呼叫的方式來進行顯示和隱藏Toast。
- NMS處理Toast的顯示和隱藏請求時會跨程式回撥TN中的方法,由於TN執行在Binder執行緒池中,故需通過Handler將其切換到當前執行緒(傳送Toast請求的執行緒)。
NMS只是起到了管理Toast佇列及其延時的效果,Toast 的顯示和隱藏實際是通過TN來實現的。
推薦閱讀:Android對話方塊Dialog,PopupWindow,Toast的實現機制
希望這篇文章對你有幫助~