window是Android视图View显示的容器,activity、Dialog、Toast中的View都是通过window来呈现的,它是一个抽象类,只有一个实现类phonewindow。windowmanager是访问window的唯一入口,window内添加、删除、更新View的操作都由windowmanager来管理,WindowManager的功能是通过WindowManagerService这个系统服务提供的,两者相互调用是一个IPC的过程。
window其实是一个抽象的概念,并没有在屏幕上有具体的存在,只是作为View的容器有window的属性。添加一个window其实是通过windowManager的addView()来实现的,该方法可以将View以window的形式显示出来。
Windowmanager.LayoutParams 有两个重要的属性flags和type。
flag 表示window的属性,可以控制window的显示特性,有很多,参考开发文档。
比如FLAG_SHOW_WHEN_LOCKED可以将window显示在锁屏界面上。
Type 参数表示window的类型,三类:应用Window,子Window,系统Window。window的类型影响window的显示层级。
应用window对应着activity层级1-99,子window常见的有dialog层级1000-1999,子Window必须附属在特定的父Window上,系统Window需要声明权限才能创建,层级2000-2999,toast,系统状态栏就是系统window。
层级越大就可以在越上层显示。
Windowmanager是用来操作window的类有添加,删除,更新。这些操作实际操作的都是window内部的View
首先从添加Window开始,添加Window其实是addView()即添加View,通过windowmanager的addView()实现,WindowManager的实现类是WindowManagerImp类,而WindowManagerImp的addView()调用了windowManagerGlobal()的addView(),类似ContextWraper调用ContextImp类的方法。可以推测windowmanager的其他两个removeview()和update也是同样的实现方式。
看看WindowManagerGlobal的addView()方法
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;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
}
ViewRootImpl root;
View panelParentView = null;
。。。。。。。。。。
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
if (mViews == null) {
index = 1;
mViews = new View[1];
mRoots = new ViewRootImpl[1];
mParams = new WindowManager.LayoutParams[1];
} else {
index = mViews.length + 1;
Object[] old = mViews;
mViews = new View[index];
System.arraycopy(old, 0, mViews, 0, index-1);
old = mRoots;
mRoots = new ViewRootImpl[index];
System.arraycopy(old, 0, mRoots, 0, index-1);
old = mParams;
mParams = new WindowManager.LayoutParams[index];
System.arraycopy(old, 0, mParams, 0, index-1);
}
index--;
mViews[index] = view;
mRoots[index] = root;
mParams[index] = 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.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
mViews[index] = view;
mRoots[index] = root;
mParams[index] = wparams;
WindowManagerglobal还将View,viewRoot,params存入了一个数组,所以每个View和ViewRoot是一一对应的,windowmanager进行add,remoteview和update时就可以将相关的参数同时添加和移除。
看到addView是通过ViewRootImp来实现绘制View的,ViewRootImp的setView()方法会绘制View,这在之前的文章中提到过View绘制原理提到的View的绘制是有ViewRoot实现的,ViewRoot的实现类是ViewRootImp。
绘制完View之后通过WindowSession完成添加window到屏幕上,其实现类是Session,是个Binder类型,用于与WindowManagerService进行进程通信。
总结一下:添加的过程.
WindowManager->WindowManagerImp->WindowManagerGlobal.addView()->ViewRootImp.setView()绘制View->Session->WindowManagerService系统进程添加Window
直接看Windowmanagerglobal的removeView()
public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
int index = findViewLocked(view, true);
View curView = removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
private int findViewLocked(View view, boolean required) {
if (mViews != null) {
final int count = mViews.length;
for (int i = 0; i < count; i++) {
if (mViews[i] == view) {
return i;
}
}
}
if (required) {
throw new IllegalArgumentException("View not attached to window manager");
}
return -1;
}
直接找到View的index,这样就可以将数组中的View移除,将被移除的对象会被放入另一个数组中。删除操作是用ViewRootImp的doDie()方法实现的会调用dispatchDetachedFromWindow()方法,该方法通过Session与WindowmanagerService通信,然后调用View的onDetachedFromWindow(),我们在自定义View时,可以在这个方法中我们进行结束动画,停止线程等操作。
大致流程和上面类似,更新会重置View的Layoutparams。最终还是由WMS来实现的。
首先在启动activity时,会创建activity的window对象,并设置其回调接口,activity实现了window的回调接口callback,所以activity可以处理window相关的事件,比如dispatchTouchEvent()方法。
activity的View是怎么添加到window上的呢?
从activity的setcontentview开始,
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
调用了Window的setContentView(),window的实现类是PhoneWindow,
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
首先生成DecorView,将Content添加到DecorView上,执行5.0以上的新增的过渡动画,将布局文件添加到Content中,最后回调window的callback的方法,oncontentchanged(),表示activity的内容已改变。
目前为止还没有将DecorView添加到window中,这步是在Activitythread的HandleResumeActivity()中执行的,回调activity的onResume()并调用windowmanager的addView()将DecorView添加到window中。
activity的window创建完成了布局文件也添加到了Window中。
Toast、Diaolog、PopupWindow、菜单、及状态栏都是通过window实现的。