1. Window 的添加过程
1.1 从源头开始
添加一个小悬浮窗是需要通过 WindowManager 的 addView 实现的,
而我们查阅官方文档发现 WindowManager 是一个接口
public interface WindowManager implements ViewManager
它的真正实现是 WindowManagerImpl ,其中有如下代码
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);
}
1.2 探寻 WindowManagerGlobal
可以看到,其中把实现 Window 的操作全部交给了 WindowManagerGlobal ,
其以工厂的形式对外提供自己的实例,其中的 addView 主要进行了如下几个工作:
1.2.1 检查参数合法性,以及调整参数
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);
}
1.2.2 创建 ViewRootImpl 并将 View 添加到列表中
先关注 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,mRoot 存储所有 Window 对应的 ViewRootImpl;mParams 存储所有 Window 对应的布局参数;mDyingViews 存储正在被删除的 View 对象,或者说已经调用 removeView 但是删除操作还没有完成的 Window。
在 addView 中通过如下方法将 Window 一系列对象添加到列表中:
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
1.2.3 通过 ViewRootImpl 来更新界面并完成 Window 的添加过程
首先,通过 ViewRootImpl 的 setView 方法:
setView 里,通过 requestLayout 完成异步刷新:
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
其中 scheduleTraversals 实际上是 View 绘制的入口。
接着,
final IWindowSession mWindowSession;
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) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
}
这里 mWindowSession 类型是 IWindowSession ,这是一个 Binder 对象,真正实现类是 Session。
也就是说 Window 的添加过程是一次 IPC 调用。
Session 内部会通过 WindowManagerService 实现 Window 的添加。
未完待续,明天再写(逃