上面3個名詞在開發中常常聽到,在Android開發中,Window是全部視圖的載體,如Activity,Dialog和Toast的視圖,咱們想要對Window進行添加和刪除就要經過WindowManager來操做,而WindowManager就是經過Binder與WindowManagerService進行跨進程通訊,把具體的實現工做交給WindowManagerService(下面簡稱WMS)。下面分別介紹它們,理清它們的基本脈絡。java
本文基於Android8.0, 相關源碼位置以下:
frameworks/base/core/java/android/view/*.java(*表明Window, WindowManager, ViewManager, WindowManagerImpl,WindowManagerGlobal, ViewRootImpl)
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
frameworks/base/services/core/java/com/android/server/wm/Session.java
複製代碼
Window在Android開發中是一個窗口的概念,它是一個抽象類,咱們打開Window,以下:android
public abstract class Window {
public static final int FEATURE_NO_TITLE = 1;
public static final int FEATURE_CONTENT_TRANSITIONS = 12;
//...
public abstract View getDecorView();
public abstract void setContentView(@LayoutRes int layoutResID);
public abstract void setContentView(View view);
public abstract void setContentView(View view, ViewGroup.LayoutParams params);
public <T extends View> T findViewById(@IdRes int id) {
return getDecorView().findViewById(id);
}
//...
}
複製代碼
能夠看到裏面有咱們熟悉的一些字段和方法,以Activity對應的Window爲例,具體的實現類是PhoneWindow,在PhoneWindow中有一個頂級View---DecorView,繼承自FrameLayout,咱們能夠經過getDecorView()得到它,當咱們調用Activity的setContentView時,其實最終會調用Window的setContentView,當咱們調用Activity的findViewById時,其實最終調用的是Window的findViewById,這也間接的說明了Window是View的直接管理者。可是Window並非真實存在的,它更多的表示一種抽象的功能集合,View纔是Android中的視圖呈現形式,繪製到屏幕上的是View不是Window,可是View不能單獨存在,它必需依附在Window這個抽象的概念上面,Android中須要依賴Window提供視圖的有Activity,Dialog,Toast,PopupWindow,StatusBarWindow(系統狀態欄),輸入法窗口等,所以Activity,Dialog等視圖都對應着一個Window。設計模式
Window的類型type被定義在WindowManager中的靜態內部類LayoutParams中,以下:session
public interface WindowManager extends ViewManager {
//...
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
//應用程序窗口type值
public static final int FIRST_APPLICATION_WINDOW = 1;//表明應用程序窗口的起始值
public static final int TYPE_BASE_APPLICATION = 1;//窗口的基礎值,其餘窗口的type值要大於這個值
public static final int TYPE_APPLICATION = 2;//普通應用程序窗口,token必須設置爲Activity的token來指定窗口屬於誰
public static final int TYPE_APPLICATION_STARTING = 3;
public static final int TYPE_DRAWN_APPLICATION = 4;
public static final int LAST_APPLICATION_WINDOW = 99;//表明應用程序窗口的結束值
//子窗口type值
public static final int FIRST_SUB_WINDOW = 1000;//表明子窗口的起始值
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;
public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
public static final int LAST_SUB_WINDOW = 1999;//表明子窗口的結束值
//系統窗口的type值
public static final int FIRST_SYSTEM_WINDOW = 2000;//表明系統窗口的起始值
public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;//系統狀態欄
public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;//搜索條窗口
public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;//通話窗口
//...
public static final int LAST_SYSTEM_WINDOW = 2999;//表明系統窗口結束值
}
}
複製代碼
LayoutParams中以TYPE開頭的值有不少,但整體能夠分爲3類:ide
type值決定了決定了Window顯示的層級(z-ordered),即在屏幕Z軸方向的顯示次序,通常狀況下type值越大,則窗口顯示的越靠前,在Window的3種類型中,應用程序窗口的層級範圍是1~99,子窗口的層級範圍是1000~1999,系統窗口的層級範圍是2000~2999,層級範圍對應着type值,若是想要Window位於全部的Window上,採用較大的層級便可,例如系統層級。函數
Window的類型flag一樣被定義在WindowManager中的靜態內部類LayoutParams中,以下:oop
public interface WindowManager extends ViewManager {
//...
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001;
public static final int FLAG_DIM_BEHIND = 0x00000002;
public static final int FLAG_BLUR_BEHIND = 0x00000004;
public static final int FLAG_NOT_FOCUSABLE = 0x00000008;
public static final int FLAG_NOT_TOUCHABLE = 0x00000010;
public static final int FLAG_NOT_TOUCH_MODAL = 0x00000020;
public static final int FLAG_KEEP_SCREEN_ON = 0x00000080;
//...
}
}
複製代碼
LayoutParams中定義的flag屬性一樣不少,這裏挑幾個常見的講解:this
能夠看到LayoutParams中的type和flag很是重要,能夠控制Window的顯示特性。知道了Window的相關信息,就能更好的瞭解WindowManager。spa
WindowManager是一個接口,裏面經常使用的方法有:添加View,更新View和刪除View,WindowManager繼承自ViewManager,這三個方法定義在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);
}
複製代碼
能夠看到這些方法傳入的參數是View,不是Window,說明WindowManager管理的是Window中的View,咱們經過WindowManager操做Window就是在操做Window中的View。WindowManager的具體實現類是WindowManagerImp,咱們看一下相應方法的實現,以下:
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Context mContext;
private final Window mParentWindow;
//...
private WindowManagerImpl(Context context, Window parentWindow) {
mContext = context;
mParentWindow = parentWindow;
}
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
//...
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
//...
mGlobal.updateViewLayout(view, params);
}
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
}
複製代碼
能夠看到WindowManagerImp也沒有作什麼,它把3個方法的操做都委託給了WindowManagerGlobal這個單例類,咱們還看到了mParentWindow這個字段,它是Window類型,是從構造中被傳入,因此WindowManager會持有Window的引用,這樣WindowManager就能夠對Window作操做了。好比mGlobal.addView,咱們能夠理解爲往window中添加View,在WindowManagerGlobal中,以下:
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow){
//...
ViewRootImpl root;
root = new ViewRootImpl(view.getContext(), display);//註釋1
//...
root.setView(view, wparams, panelParentView);
}
複製代碼
最終會走到ViewRootlmp的setView中, 以下:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
//...
//這裏會進行View的繪製流程
requestLayout();
//...
//經過session與WMS創建通訊
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
//...
}
複製代碼
在ViewRootlmp的setView中,首先經過requestLayout()發起View繪製流程,而後在mWindowSession的addToDisplay中經過Binder與WMS進行跨進程通訊,請求顯示窗口上的視圖,至此View就會顯示到屏幕上。這個mWindowSession是一個IWindowSession.AIDL接口類型,用來實現跨進程通訊,在WMS內部會爲每個應用的請求保留一個單獨的Session,一樣實現了IWindowSession接口,應用與WMS之間的通訊就經過這個Session。那麼這個mWindowSession何時被賦值的呢?就在上面的註釋1中,咱們打開ViewRootlmp的構造函數,以下:
public ViewRootImpl(Context context, Display display) {
mWindowSession = WindowManagerGlobal.getWindowSession();
//...
}
複製代碼
能夠看到mWindowSession是經過WindowManagerGlobal的單例類的getWindowSession()得到的,咱們打開WindowManagerGlobal的getWindowSession(),以下:
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
//一、首先獲取WMS的本地代理
IWindowManager windowManager = getWindowManagerService();
//二、經過WMS的本地代理的openSession來獲取Session
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
複製代碼
咱們首先看1,getWindowManagerService()源碼以下:
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
//獲取WMS的本地代理對象
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
//...
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowManagerService;
}
}
複製代碼
能夠看到, ServiceManager.getService("window")就是得到WMS,而後經過IWindowManager.Stub.asInterface()轉換成WMS在應用進程的本地代理,getWindowManagerService()就是返回WMS在本地應用進程的代理。(這裏涉及到Binder知識)
而後看2,經過WMS的本地代理的openSession來獲取Session,咱們能夠在WMS中找到這個函數實現,以下:
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client, IInputContext inputContext) {
//...
//爲每一個窗口請求建立一個Session並返回
Session session = new Session(this, callback, client, inputContext);
return session;
}
複製代碼
至此創建起與WMS的通訊的橋樑。而後WindowManager就間接的經過Session向WMS發起顯示窗口視圖的請求,WMS會嚮應用返回和窗口交互的信息。至於mGlobal.updateViewLayout和mClobal.removeView也是相似的過程,可自行研究。
WindowManagerService是一個系統級服務,由SystemService啓動,實現了IWindowManager.AIDL接口,它的主要功能分爲如下倆方面:
它負責窗口的啓動,添加和刪除,它還負責窗口的層級顯示(z-orderes)和維護窗口的狀態。咱們繼續上面的mGlobal.addView,上面講到這個方法是向WMS發起一個顯示窗口視圖的請求,最終會走到mWindowSession.addToDisplay()方法,咱們能夠在Session中找到這個函數實現,以下:
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) {
//返回WMS中addWindow所返回的結果
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
複製代碼
能夠看到addToDisplay方法中最終返回了WMS中addWindow所返回的結果,Window的添加請求就交給WMS去處理,addWindow的實如今WMS中,裏面代碼很長,這裏就再也不深究了(留在下一篇文章從一個例子分析),addWindow主要作的事情是先進行窗口的權限檢查,由於系統窗口須要聲明權限,而後根據相關的Display信息以及窗口信息對窗口進行校對,再而後獲取對應的WindowToken,再根據不一樣的窗口類型檢查窗口的有效性,若是上面一系列步驟都經過了,就會爲該窗口建立一個WindowState對象,以維護窗口的狀態和根據適當的時機調整窗口狀態,最後就會經過WindowState的attach方法與SurfaceFlinger通訊。所以SurfaceFlinger能使用這些Window信息來合成surfaces,並渲染輸出到顯示設備。
當咱們的觸摸屏幕時就會產生輸入事件,在Android中負責管理事件的輸入是InputManagerService,在啓動IMS的時候會在native層建立NativeInputManager,在NativeInputManager的構造中會建立InputManager和Eventhub(監聽/dev/input/設備節點中全部事件的輸入),在InputManager構造中會依此建立InputDispatcher、InputReader、InputReaderThread、InputDispatcherThread。
InputReader運行在InputReaderThread中,它會不斷循環從EventHub(調用EventHub的getEvent()方法)中讀取原始輸入事件,InputReader將這些原始輸入事件加工後就交給運行在InputDispatcherThread中的InputDispatcher,而InputDispatcher它會尋找一個最合適的窗口來處理輸入事件,WMS是窗口的管理者,WMS會把全部窗口的信息更新到InputDispatcher中,這樣InputDispatcher就能夠將輸入事件派發給合適的Window,Window就會把這個輸入事件傳給頂級View,而後就會涉及咱們熟悉的事件分發機制。
咱們來再來看在ViewRootImp的setView中調用mWindowSession.addToDisplay方法時傳入的參數:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
//...
mInputChannel = new InputChannel();
//...
//經過session與WMS創建通訊,同時經過InputChannel接收輸入事件回調
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
//...
if (mInputChannel != null) {
//...
//處理輸入事件回調
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());
}
}
複製代碼
注意這個傳入的mInputChannel參數,它實現了Parcelable接口,用於接受WMS返回來的輸入事件。
它們之間的類圖關係以下:
經過上面簡單的介紹,咱們知道Window是View的載體,咱們想要對Window進行刪除,添加,更新View就得經過WindowManager,WindowManager與WMS經過Session進行通訊,具體的實現就交給了WMS處理,WMS會爲每個Window建立一個WindowState並管理它們,具體的渲染工做WMS就交給SurfaceFinger處理。本文所討論的WMS系統相關結構以下:
參考資料:
《Android源碼設計模式》