介绍
Window表示一个窗口的概念,它是一个抽象类,其唯一的实现类是PhoneWindow。Android中所有视图的显示都需要通过Window来呈现,可以说只要有视图的地方就有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方法有两个参数,第一个是要添加的View,第二个是WindowManager.LayoutParams对象,LayoutParams有一个type属性,它代表要添加的Window的类型,分为三类。
Window的分类
-
应用Window
Activity的窗口类型就是应用Window
-
子Window
子Window不能单独存在,需要依附于父Window上,如Dialog、PopWindow等
-
系统Window
系统Window是系统层级的Window,如状态栏、Toast等
Window是分层显示的,每个Window都有其对应的z-ordered,层级大的会覆盖在层级小的上面。其中应用Window的层级范围为1-99,子Window层级范围为1000-1999,系统Window层级范围为2000-2999,该层级范围对应着WindowManager.LayoutParams的type属性。上面添加Window的type为FIRST_SUB_WINDOW,它是子Window的初始取值,所以我们添加的是一个子Window。
Window的内部机制
我们在使用WindowManager添加Window时,其实是添加了一个View。Window作为一个抽象概念,它并不是实际存在的,而是以View的形式存在的,每一个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系。
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的实现类是WindowManagerImpl,addView方法
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
这里没有具体实现,转而调用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的类型判断,然后会查找当前添加的View是否已经存在,如果已经存在并且它在等待移除队列中的话,直接将其移除,否则抛出View已经添加过的异常。判断添加的Window类型为子Window的话,还要找到它所附属的父Window。
- 初始化ViewRootImpl对象,并将LayoutParams设给View。
- 将View,ViewRootImpl以及LayoutParams对象添加到对应的集合中。
- 调用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方法中调用了scheduleTraversals,而这个scheduleTraversals就是View绘制流程的起点,在这里完成了View树的绘制。
mWindowSession是一个Binder对象,它的实现是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
最后调用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设置给View,找到当前View的index索引,根据index移除旧的LayoutParams并将新的LayoutParams添加到集合中。调用ViewRootImpl的setLayoutParams方法
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
//...
scheduleTraversals();
//...
}
这里调用scheduleTraversals方法对布局进行重绘,完成了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的索引,然后调用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);
}
}
}
调用ViewRootImpl的die方法
boolean die(boolean immediate) {
//...
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
//...
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
immediate参数表示是否要立即移除,即同步还是异步。我们一般采用异步移除的方式,异步移除使用mHandler发送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方法中会回调View的onDetachedFromWindow方法,以及调用Session的remove方法,该方法最终还是调用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方法中刷新数据,移除对应的ViewRootImpl,LayoutParams以及将View从mDyingViews中移除,这就完成了Window的删除。
可以发现不管是添加,更新还是删除Window,最后都是在操作View,所以这就能证明Window并不是实际存在的,它是以View的形式存在,而Window是View的直接管理者。
网友评论