1.概述
- 所有视图都通过Window来呈现,包括Activity/Dialog/Toast等,Window是View的直接管理者;
- WindowManager是外界访问Window的入口;
- Window的具体实现在WindowManagerService中,WindowManage与WindowManagerService的交互式IPC过程;
- window分为3个层级,WindowManager.LayoutParams.type参数来控制。
WindowManager.LayoutParams.type的取值范围
应用window的层级:1~99
子window的层级:1000~1999
系统window的层级:2000~2999 (需要申请权限)
2.关键类图
3.重点关注WindowManager的3个方法
addView / updateViewLayout / removeView
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
/**
* The interface that apps use to talk to the window manager.
**/
public interface WindowManager extends ViewManager {
...
}
3.1 添加Window的过程
- 检查参数是否合法,若是子Window还需进一步调整布局参数;
- 创建ViewRootImpl并将View添加到列表中,桥接模式由WindowManagerGlobal来完成;
- 通过ViewRootImpl的setView方法完成更新界面并完成Window的添加过程。
WindowManager.addView
-> WindowManagerGlobal.addView
-> ViewRootImpl.setView
-> WindowManagerService
3.1.1 WindowManager#addView()
WindowManager是一个接口,实现类为WindowManagerImpl.
WindowManagerImpl#addView()
public final class WindowManagerImpl implements WindowManager {
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);
}
...
}
WindowManagerImpl的实现是依赖WMG。
3.1.2 WindowManagerGlobal#addView()
public final class WindowManagerGlobal {
...
// 保持view的集合以及参数
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>();
...
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
...
}
}
1.WMG维护4个重要的集合:mViews, mRoots, mParams, mDyingViews
2.在WMG#addView()方法维护前面3个集合,
3.在WMG#removeView()方法中维护mDyingViews
3.1.3 ViewRootImpl#setView()
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
requestLayout();
...
try {
...
// 说明1.mWindowSession是一个Binder类型
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
} catch (RemoteException e) {
...
} finally {
...
}
...
// 说明2. 接收触屏事件
// Set up the input pipeline.
CharSequence counterSuffix = attrs.getTitle();
mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
}
ViewRootImpl的setView方法主要完成2件事
- 完成view的渲染
mWindowSession是Session对象,是通过AIDL的方式调用WMS的openSession()方法获取的。- 接收触屏事件,ViewRootImpl - DecorView - window.cb.dispatchTouchEvent - cb就是Activity - PhoneWindow - DecorView, 可见触屏事件的传递是从DecorView路过Activity,最后又回到DecorView。
mWindowSession = WindowManagerGlobal.getWindowSession();
WMG.getWindowSession()
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
// 说明1
IWindowManager windowManager = getWindowManagerService();
// 说明2
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
- getWindowManagerService()就是标准的AIDL调用,返回WMS
- 获取了WMS接口实例,然后调用WMS#openSession(),返回Session对象
Session#addToDisplay
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets, Rect outOutsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
}
mService就是WMS,添加window的工作从这里传到WMS。
Activity-Window-View
- activity#setContentView(layoutResID)
- PhoneWindow#setContentView(layoutResID)
- 初始化DecorView,它是FrameLayout的子类
installDecor()
mLayoutInflater.inflate(layoutResID, mContentParent);
DecorView-ViewRootImpl
- Activity的onResume方法将DecorView添加到windowManager
ActivityThread#handleResumeActivity(…)
具体几处体现如下:
View decor = r.window.getDecorView();
ViewManager wm = a.getWindowManager();
wm.addView(decor, l);- 进入window的添加过程 WMG-ViewRootImpl-WMS
3.2 删除Window的过程
- 找到待删除View的索引index;
- removeViewLocked方法内部通过ViewRootImpl的die方法完成,并将view加到mDyingViews列表中。
WindowManager.removeView
-> WindowManagerGlobal.removeView
-> ViewRootImpl.die
3.3 更新Window的过程
- 更新View的LayoutParams并替换老的LayoutParams;
- 更新ViewRootImpl中的LayoutParams.
WindowManager.updateViewLayout
-> WindowManagerGlobal.updateLayoutView
-> ViewRootImpl.setLayoutParams
4. 总结
- 一个Activity对应一个PhoneWindow;
PhoneWindow是在activity.attach()内创建Activity - PhoneWindow - ViewRootImpl
它们3者是一一对应的 - WMG是单例获取的,所以一个进程中只有一个;
- 一个PhoneWindow对应一个ViewRootImpl;
WM#addView() -> WMG#addView() 此方法内会创建 ViewRootImpl对象,并保存在mRoots集合中 - ViewRootImpl#setView()方法很重要,完成view渲染、接收触屏事件。