介紹
Window表示一個窗口的概念,它是一個抽象類,其唯一的實(shí)現(xiàn)類是PhoneWindow。Android中所有視圖的顯示都需要通過Window來呈現(xiàn),可以說只要有視圖的地方就有Window,比如Activity,Dialog它們的視圖也是需要附加在Window上顯示的,所以Window可以說是View的直接管理者。
WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Button button = new Button(this);
button.setText("我是Button");
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.FIRST_SUB_WINDOW, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT
);
windowManager.addView(button, layoutParams);
以上就是用WindowManager添加一個Window的過程,addView方法有兩個參數(shù),第一個是要添加的View,第二個是WindowManager.LayoutParams對象,LayoutParams有一個type屬性,它代表要添加的Window的類型,分為三類。
Window的分類
-
應(yīng)用Window
Activity的窗口類型就是應(yīng)用Window
-
子Window
子Window不能單獨(dú)存在,需要依附于父Window上,如Dialog、PopWindow等
-
系統(tǒng)Window
系統(tǒng)Window是系統(tǒng)層級的Window,如狀態(tài)欄、Toast等
Window是分層顯示的,每個Window都有其對應(yīng)的z-ordered,層級大的會覆蓋在層級小的上面。其中應(yīng)用Window的層級范圍為1-99,子Window層級范圍為1000-1999,系統(tǒng)Window層級范圍為2000-2999,該層級范圍對應(yīng)著WindowManager.LayoutParams的type屬性。上面添加Window的type為FIRST_SUB_WINDOW,它是子Window的初始取值,所以我們添加的是一個子Window。
Window的內(nèi)部機(jī)制
我們在使用WindowManager添加Window時,其實(shí)是添加了一個View。Window作為一個抽象概念,它并不是實(shí)際存在的,而是以View的形式存在的,每一個Window都對應(yīng)著一個View和一個ViewRootImpl,Window和View通過ViewRootImpl來建立聯(lián)系。
WindowManager接口繼承了ViewManager接口,在ViewManager中分別定義了添加,更新和刪除Window的方法
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
分別來看Window的添加,更新和刪除過程
Window的添加
WindowManager的實(shí)現(xiàn)類是WindowManagerImpl,addView方法
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
這里沒有具體實(shí)現(xiàn),轉(zhuǎn)而調(diào)用mGlobal的方法,mGlobal為WindowManagerGlobal對象
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
//...
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
//...
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
- 首先做View等的非空判斷以及LayoutParams的類型判斷,然后會查找當(dāng)前添加的View是否已經(jīng)存在,如果已經(jīng)存在并且它在等待移除隊(duì)列中的話,直接將其移除,否則拋出View已經(jīng)添加過的異常。判斷添加的Window類型為子Window的話,還要找到它所附屬的父Window。
- 初始化ViewRootImpl對象,并將LayoutParams設(shè)給View。
- 將View,ViewRootImpl以及LayoutParams對象添加到對應(yīng)的集合中。
- 調(diào)用ViewRootImpl的setView方法完成布局的刷新以及Window的添加。
ViewRootImpl的setView方法
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
//...
requestLayout();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
//...
}
}
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
在requestLayout方法中調(diào)用了scheduleTraversals,而這個scheduleTraversals就是View繪制流程的起點(diǎn),在這里完成了View樹的繪制。
mWindowSession是一個Binder對象,它的實(shí)現(xiàn)是Session
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets
最后調(diào)用WindowManagerService的addWindow方法完成了Window的添加。
Window的更新
Window的更新過程,直接來看WindowManagerGlobal的updateViewLayout方法
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
//...
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
view.setLayoutParams(wparams);
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
root.setLayoutParams(wparams, false);
}
}
將新的LayoutParams設(shè)置給View,找到當(dāng)前View的index索引,根據(jù)index移除舊的LayoutParams并將新的LayoutParams添加到集合中。調(diào)用ViewRootImpl的setLayoutParams方法
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
//...
scheduleTraversals();
//...
}
這里調(diào)用scheduleTraversals方法對布局進(jìn)行重繪,完成了Window的更新。
Window的刪除
WindowManagerGlobal的removeView方法
public void removeView(View view, boolean immediate) {
//...
synchronized (mLock) {
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate);
//...
}
}
獲取要移除View的索引,然后調(diào)用removeViewLocked方法
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
//...
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
調(diào)用ViewRootImpl的die方法
boolean die(boolean immediate) {
//...
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
//...
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
immediate參數(shù)表示是否要立即移除,即同步還是異步。我們一般采用異步移除的方式,異步移除使用mHandler發(fā)送MSG_DIE消息,然后返回true,在removeViewLocked方法中將該View添加到等待移除的列表mDyingViews中。mHandler中處理MSG_DIE消息的方法為doDie
void doDie() {
//...
if (mAdded) {
dispatchDetachedFromWindow();
}
//...
WindowManagerGlobal.getInstance().doRemoveView(this);
//...
}
void dispatchDetachedFromWindow() {
if (mView != null && mView.mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
mView.dispatchDetachedFromWindow();
}
//...
try {
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
//...
}
在dispatchDetachedFromWindow方法中會回調(diào)View的onDetachedFromWindow方法,以及調(diào)用Session的remove方法,該方法最終還是調(diào)用WMS的removeWindow方法完成Window的移除。
void doRemoveView(ViewRootImpl root) {
synchronized (mLock) {
final int index = mRoots.indexOf(root);
if (index >= 0) {
mRoots.remove(index);
mParams.remove(index);
final View view = mViews.remove(index);
mDyingViews.remove(view);
}
}
//...
}
最后在doRemoveView方法中刷新數(shù)據(jù),移除對應(yīng)的ViewRootImpl,LayoutParams以及將View從mDyingViews中移除,這就完成了Window的刪除。
可以發(fā)現(xiàn)不管是添加,更新還是刪除Window,最后都是在操作View,所以這就能證明Window并不是實(shí)際存在的,它是以View的形式存在,而Window是View的直接管理者。