本文源码基于API 26
参考资料 Andriod开发艺术探索
类图
流程图
1 Window的添加过程
Window的添加是通过WindowManager的addView()来实现,WindowManager是一个接口,他的真正实现是WindowManagerImpl类
1.1 WindowManagerImpl
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
可以发现,WIndowManagerImpl并没有实现Window的三种方法,而是全部交给了WindowManagerGlobal来处理。
1.2 WindowManagerGlobal
1.2.1 addView()
WindowManagerGlobal的addView()方法主要分为如下几步
1 检查参数是否合法,如果是子Window那么还要调整一些布局参数
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内部的几个比较重要的列表
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>();
mViews存储的是所有的Window所对应的View,
mRoots存储的是WIdnow对应的ViewRootImpl
mParams存储的是所有Window所对应的布局参数,
mDingViews则存储了哪些正在被删除的View对象。或者说是那些已经调用了removeView方法但是删除操作还未完成的Window对象,在addview中通过如下方式将Window的一系列对象添加到列表中。
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
3 通过ViewRootImpl来更新界面并完成Window的添加过程
root.setView(view, wparams, panelParentView);
1.3 ViewRootImpl
1.3.1 setView()
在ViewRootImpl中的setView()中会通过requestLayout方法来完成一部刷新请求。
requestLayout();
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
接着会通过WindowSession来完成Window的添加过程。在下面的代码中,mWindowSession的类型是IWindowSession,它是一个Binder对象,真正的实现类是Session,也就是WIndow的添加过程是一次IPC调用。
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
1.4 Session
1.4.1 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);
}
Session内部会通过WindowManagerService来实现Window的添加。
2 Window的删除过程
2.1 WindowManagerGloba
2.1.1 findViewLocked()
Window的删除过程和添加过程一样,都是先通过WindowManagerImpl后,再进一步通过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);
}
}
在此方法中,首先通过findViewLocked方法来查找待删除的View的索引,然后通过removeViewLocked方法来做进一步的删除。
2.1.2 removeViewLocked
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);
}
}
}
在此方法中,通过ViewRootImpl来完成删除操作。
2.2 ViewRootImpl
2.2.1 die()
boolean die(boolean immediate) {
// Make sure we do execute immediately if we are in the middle of a traversal or the damage
// done by dispatchDetachedFromWindow will cause havoc on return.
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;
}
首先会判断是否是异步删除,
如果是异步删除,就直接发送一个MSG_DIE的消息。ViewRootImpl中的Handler会处理此消息并调用doDie方法。
如果是同步删除,则直接调用doDie方法。
在doDie方法中会调用dispatchDetachedFromWindow方法。
最后会调用WindowManagerGlobal的doRemoeView方法,将当前Window所关联的这三类对象从列表中移除
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();
}
}
2.2.2 dispatchDetachedFromWindow()
在dispatchDetachedFromWindow方法中主要做了如下几件事
1 释放对象,清楚数据和消息,移除回调
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;
}
2 通过Session的remove()方法移除window
mWindowSession.remove(mWindow);
3 调用View的dispatchDetachedFromWindow方法
if (mView != null && mView.mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
mView.dispatchDetachedFromWindow();
}
调用View的dispatchDetachedFromWindow方法,并在dispatchDetachedFromWindow方法的内部会调用onDetachedFromWindow和onDetachedFromWindowInternal方法。onDetachedFromWindow方法一般会做一些资源的回收等。
同样还是来看WindowManagerGlobal的updateViewLayout方法
3 Window的更新过程
3.1 WindowManagerGlobal
3.1.1 updateViewLayout()
1 首先更新View的LayoutParams
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
view.setLayoutParams(wparams);
2 再更新ViewRootImpl的LayoutParams
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
root.setLayoutParams(wparams, false);
}
在ViewRootImpl中经过一系列的调用,setLayoutParams最终会调用performTraversals方法.
3.2 ViewRootImpl
3.2.1 performTraversals()
在此方法中来对View重新测量,布局,绘制。
测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
布局
performLayout(lp, mWidth, mHeight);
绘制
performDraw
在测量,布局,绘制之前调用了relayoutWindow方法。
3.2.2 relayoutWindow()
在relayoutWindow方法中,会通过WindowSession来更新Window的视图,这个过程最终通过WindowManagerService的relayoutWindow方法来实现。
int relayoutResult = mWindowSession.relayout(
mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f),
viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame,
mPendingMergedConfiguration, mSurface);