前言
我们知道,在 Windows 操作系统中,每一项任务都是在一个打开的窗口中进行的,窗口的概念非常好理解。而在 Android 中,其实窗口也是一个非常重要的概念,但是却很少被我们接触。
其实,和 Windows 操作系统一样,Android 中的每一个视图,例如一个 Activity、一个 Dialog 或者一个 Toast,它们都是一个窗口,这些窗口来自不同的进程,却全部由一个系统服务(WindowManagerService)统一管理。
我们也可以直接利用 WindowManager 来控制自己的 Window。
Window 的添加过程
WindowManager 有三个方法:
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
这也是 WindowManager 提供的所有给开发者的方法,可以看到,这些方法都是直接操作 View 本身,而完全看不到 Window 的踪迹。其实,Window 是一个抽象的概念,具体的显示仍然是 View 的工作。
任何视图的显示都是通过 View,而任何 View 必须是依附于一个 Window,才能被显示。
一个 Window 包含着一个 View 和一个 ViewRootImpl,View 即需要被显示的 View 层次的根 View,而 ViewRootImpl 负责 Window 和 View 之间的沟通。
Window 被添加需要通过 WindowManager 的 addView 方法,而 WindowManager 是一个接口类,addView 真正的实现是在 WindowManagerImpl 中:
@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);
}
可以看到,WindowManagerImpl 又将所有操作委托给了 WindowManagerGlobal 来实现,WindowManagerGlobal 的 addView主要分为几个步骤:
1. 检查参数是否合法,如果 Window 类型是 SubWindow(1000-1999),则还需要调整一些参数
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);
}
2. 创建 ViewRootImpl 并将 View 添加到列表中
因为 WindowManagerGlobal 管理的是整个系统的 Window,在 WindowManagerGlobal 中,维护着几个列表:
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
private final ArraySet<View> mDyingViews = new ArraySet<View>();
其中,mView 对应着所有 Window 中的 View 层次的根 View,mRoots 对应着所有 Window 中的 ViewRootImpl,mParams 对应着所有 Window 中的布局参数,而 mDyingViews 保存了那些已经调用了 removeView 但还没删除的 View。
在 addView 接下来的步骤中,将要被添加的 Window 的 ViewRootImpl 被创建。
ViewRootImpl root;
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
3. 通过 ViewRootImpl 来更新界面,并完成 Window 的添加
addView 方法的最后,调用 ViewRootImpl 的 setView 方法
root.setView(view, wparams, panelParentView);
而在 setView 方法中,首先调用了 requestLayout() 方法
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
这里的 scheduleTraversals 方法,其实就是 View 层级的整个测量、布局和绘制迭代过程的入口,也就是说,到这里此 Window 中的 View 层级的绘制过程已经开始。
在 setView 方法的最后,会通过 WindowSession 的 addToDisplay 方法,执行添加 Window 的最后一步:
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(
mSeq,
mWindowAttributes,
getHostVisibility(),
mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets,
mInputChannel
);
}
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) {
// 最终将 Window 添加请求交给 WindowManagerService 处理了
return mService.addWindow(
window,
seq,
viewVisibility,
displayId,
outStableInsets,
outOutsets,
outInputChannel
);
}
这里的 WindowSession 是一个 IBinder 对象,连接系统进程 WindowManagerService,前面说过,WindowManagerService 管理整个 Android 系统的 Window,因此只有在 WindowManagerService 这里“登记”的 Window,才算是真正添加完成。进入 WindowManagerService 的 addWindow 方法中,可以看到大量的添加 Window 最终步骤的代码细节,至此,整个添加 Window 的大致流水帐就过了一遍。
Window 的 remove 和 update
Window 的 remove 和 update 和 add 操作相似,具体的顺序均为:
romove
WindowManager - > WindowManagerImpl -> WindowManagerGlobal -> ViewRootImpl .die() -> ViewRootImpl.dispatchDetchedFromWindow() -> WindowSession.removeWindow()
update
WindowManager - > WindowManagerImpl -> WindowManagerGlobal -> ViewRootImpl .setLayoutParams() -> ViewRootImpl.pokeDrawLockIfNeeded() -> WindowSession.pokeDrawLock(Window)
总结起来,Window 通过 ViewRootImpl 管理整个 View 层次,又总是最终通过 ViewRootImpl 调用 IBinder 对象 WindowSession 的方法,将最终步骤交付到 WindowManagerService 处。