前言
Window表示一个窗口, Android中所有视图都是通过Window来呈现的, 例如Activity, Dialog, Toast, PopupWindow等等, 所以说Window是View的直接管理者.
Window的应用
WindowManager是外界访问Window的入口, WindowManager继承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就是通过这三个方法实现View的添加,更新和删除. 下面一个简单的demo通过WindowManger添加一个Window.
Button button = new Button(this);
button.setText("button");
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0, PixelFormat.TRANSPARENT);
layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
layoutParams.gravity = Gravity.LEFT|Gravity.TOP;
layoutParams.x = 100;
layoutParams.y = 100;
WindowManager windowManager = getWindowManager();
windowManager.addView(button, layoutParams);
该Demo可以将一个Button添加到屏幕左上角坐标为(100, 100)的位置.
- type属性
type表示Window的类型, 分应用Window, 子Window, 系统Window.
- 应用Window对应一个Activity.
- 子Window不能单独存在, 需要附属在特定的父Window中, 例如Dialog.
- 系统Window需要声明SYSTEM_ALERT_WINDOW权限才能创建, 例如Toast.
注意: Window是分层的, 层级大的会覆盖在层级小的Window上面, 应用Window的层级范围是1~99, 子Window的层级范围是1000~1999, 系统Window的层级范围是2000~2999.如果想要Window显示在所有Window最顶层, 只需要将type设置为较大的层级即可.
- flags属性
flags表示Window的属性, 常用的有以下几种:
- FLAG_NOT_FOCUSABLE: 表示Window不能获取焦点和输入事件, 最终事件会传递到下层具有焦点的Window.此标记会开启FLAG_NOT_TOUCH_MODAL属性.
- FLAG_NOT_TOUCH_MODAL:设置了该属性, 即使Window获取焦点时(没有设置FLAG_NOT_FOCUSABLE), Window以外的事件会传递到下层Window, Window以内的事件自己处理. 如果不设置该属性, 该Window外部和内部的事件都会自己处理.
- FLAG_NOT_TOUCHABLE: 表示Window不接受触摸事件.
Window内部机制
在实际使用中无法直接访问Window, 对Window的访问必须通过WindowManager, 而WindowManager实现的三个方法addView, updateViewLayout,removeView都是针对View的, 所以View才是Window存在的实体.
WindowManager是一个接口, 它的具体实现是WindowManagerImpl, 在WindowManagerImpl中对Window的三大操作如下:
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
WindowManagerImpl并没有直接实现Window的三大操作, 而是将所有操作都委托给了WindowManagerGlobal. WindowManagerImpl的全局变量中通过单例模式实例化WindowManagerGlobal, 说明一个进程只有一个WindowManagerGlobal对象.
下面看WindowManagerGlobal中的三个具体实现.
Window的添加过程
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;
//如果是子Window, 需要调整布局参数
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
//不是子Window, 检查是否设置硬件加速
final Context context = view.getContext();
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
//设置系统参数改变的回调
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
int index = findViewLocked(view, false);
//如果该View已经被添加到WindowManager, 如果该View正在被删除, 则立即删除, 否则抛异常, 提示该View已经被添加
if (index >= 0) {
//mDyingViews列表保存正在被删除的View
if (mDyingViews.contains(view)) {
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
}
// 如果是子Window, 找出它所附属的Window
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);
}
}
}
//创建ViewRootImpl, 保存该Window对应的View, ViewRootImpl, 以及布局参数
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);//mViews保存要操作的View
mRoots.add(root);//mRoots保存View对应的ViewRootImpl
mParams.add(wparams);//mParams保存View的布局参数
//通过ViewRootImpl完成View的添加
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
添加过程大致分以下几步:
- 检查参数是否合法, 如果是子Window需要调整布局参数.
- 创建ViewRootImpl, 并将该Window对应的View, ViewRootImpl, 布局参数做保存.
- 通过ViewRootImpl完成View的添加.
ViewRootImpl实现了View和WindowManager之间的通信协议, 是
WindowManagerGlobal操作View的具体实现.
下面看ViewRootImpl的setView方法.
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
......
//添加View之前先调用layout布局, 以确保接受任何系统事件之前重新布局
requestLayout();//View的绘制流程
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
......
} finally {
......
}
......
}
}
setView方法中通过requestLayout完成View的异步刷新, requestLayout最终调用performTraversals完成View的measure, layout, draw过程.
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
......
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
......
performTraversals();
......
}
}
private void performTraversals() {
......
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
......
performLayout(lp, mWidth, mHeight);
......
performDraw();
......
}
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
......
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
......
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
......
}
private void performDraw() {
......
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
try {
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
......
}
然后通过mWindowSession.addToDisplay完成Window的添加, mWindowSession是IWindowSession, 是一个Binder对象, 最终实现类是Session, 因此Window的添加是一个IPC过程.
下面看Session的addToDisplay方法.
@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, outStableInsets, outOutsets, outInputChannel);
}
mService是WindowManagerService, 由此可见, Window的添加最终交给WindowManagerService去处理, 在WindowManagerService内部会为每个应用保留一个单独的Session. 在WindowManagerService中会创建一个WindowState对象用来表示当前添加的窗口, WindowManagerService负责管理这里些WindowState对象.
至此, Window的添加过程就结束了.
Window的更新过程
Window的更新也是通过WindowManagerGlobal实现的, 下面看WindowManagerGlobal的updateViewLayout方法.
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
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);
}
}
更新过程比较简单, 首先替换掉view的LayoutParams, 然后通过findViewLocked找到该View的索引, 更新mParams列表中对应的布局参数, 最后通过ViewRootImpl的setLayoutParams实现Window的更新.
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
......
scheduleTraversals();
}
}
由上可知, scheduleTraversals方法最终调用performTraversals实现View的measure, layout, draw过程完成重新布局. 除了View本身的重绘以外, performTraversals方法中还会调用relayoutWindow, relayoutWindow中通过WindowSession的relayout方法更新Window, 这个过程最终又交给WindowManagerService的relayoutWindow实现.
Window的删除过程
同样, Window的删除也是通过WindowManagerGlobal.看下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 = mRoots.get(index).getView();
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
首先通过findViewLocked找到要删除View的索引, 在removeViewLocked方法中找到该View对应的ViewRootImpl, 然后通过ViewRootImpl的die方法完成Window的删除.下面看下ViewRootImpl的die方法.
boolean die(boolean immediate) {
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
if (!mIsDrawing) {
destroyHardwareRenderer();
} else {
Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
" window=" + this + ", title=" + mWindowAttributes.getTitle());
}
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
final class ViewRootHandler extends Handler {
......
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
......
case MSG_DIE:
doDie();
break;
}
}
}
immediate表示同步删除, WindowManager提供了两种删除方法, removeView和removeViewImmediate, 前者为异步删除, 后者为同步删除.
上面WindowManagerImpl的removeView方法调用了WindowManagerGlobal的removeView(view, false), immediate为false, 因此WindowManager的删除属于异步操作.
那么同步删除和异步删除有什么区别呢,
通过die这段代码可知, 如果是同步删除(immediate为true), 直接调用doDie()方法, 如果是异步删除(immediate为false), 通过Handler发送一个MSG_DIE的消息后立即返回true了, 然后将该View加入到mDyingViews中, 此时该View并没有被删除, 只有当消息队列循环处理到该消息时才执行doDie方法.
下面看下ViewRootImpl的doDie方法.
void doDie() {
......
synchronized (this) {
if (mRemoved) {
return;
}
mRemoved = true;
//setView时mAdded置为true, doDie之后置为false
if (mAdded) {
dispatchDetachedFromWindow();
}
......
mAdded = false;
}
WindowManagerGlobal.getInstance().doRemoveView(this);
}
首先调用dispatchDetachedFromWindow.
void dispatchDetachedFromWindow() {
if (mView != null && mView.mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
mView.dispatchDetachedFromWindow();
}
//移除各种监听
mAccessibilityInteractionConnectionManager.ensureNoConnection();
mAccessibilityManager.removeAccessibilityStateChangeListener(
mAccessibilityInteractionConnectionManager);
mAccessibilityManager.removeHighTextContrastStateChangeListener(
mHighContrastTextManager);
removeSendWindowContentChangedCallback();
destroyHardwareRenderer();
setAccessibilityFocus(null, null);
//清空数据
mView.assignParent(null);
mView = null;
mAttachInfo.mRootView = null;
mSurface.release();
if (mInputQueueCallback != null && mInputQueue != null) {
mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
mInputQueue.dispose();
mInputQueueCallback = null;
mInputQueue = null;
}
if (mInputEventReceiver != null) {
mInputEventReceiver.dispose();
mInputEventReceiver = null;
}
try {
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
// Dispose the input channel after removing the window so the Window Manager
// doesn't interpret the input channel being closed as an abnormal termination.
if (mInputChannel != null) {
mInputChannel.dispose();
mInputChannel = null;
}
mDisplayManager.unregisterDisplayListener(mDisplayListener);
unscheduleTraversals();
}
该方法主要内容:
- 调用View的dispatchDetachedFromWindow方法, 内部又调用了View的onDetachedFromWindow方法, 因此, 当通过WindowManager移除View时会调用View的onDetachedFromWindow方法.
- 移除各种监听, 清空数据.
- 通过WindowSession的remove删除Window, 该方法最终调用了WindowManagerService的removeWindow, 及Window的删除由ViewRootImpl交给了WindowManagerService.
然后通过WindowManagerGlobal的doRemoveView更新数据.
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);
}
}
if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
doTrimForeground();
}
}
该方法是在Window删除之后更新参数信息, 包括mRoots, mParams,mViews,mDyingViews.
总结
综上, Window的三大操作都是由WindowManager发起, 交给WindowManagerGlobal处理, 然后通过ViewRootImpl实现View与WindowManager的通信. 大致流程如图所示: