Window
并不是实际存在的,它是以View
的形式存在,WindowManager
中的三个方法addView
、updateViewLayout
、removeView
也都是基于View的,这说View
才是Window
存在的实体。在实际使用中是无法直接访问Window
的,对Window
的访问必须通过WindowManager
,分析Window
的内部机制从它的添加、更新、删除入手。
WindowManager是一个接口,它的具体实现是WindowManagerImpl,在WindowManagerImpl中的三个方法实现如下:
//WindowManager
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
@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.Window的添加过程
- 检查参数是否合法,如果是子View还需要调整一些布局参数
在WindowManagerImpl中,addView最终是交给了WindowManagerGlobal去实现,在WindowManagerGlobal中的addView方法中做的第一步就是检查参数的合法性,代码如下:
//WindowManagerImpl#addView
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
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);
}
...
}
- 创建ViewRootImpl并将View添加到列表中
@UnsupportedAppUsage
private final ArrayList<View> mViews = new ArrayList<View>();
@UnsupportedAppUsage
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
@UnsupportedAppUsage
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
private final ArraySet<View> mDyingViews = new ArraySet<View>();
在WindowManagerGlobal类中创建了以上几个列表,,mViews负责存储Window所对应的View,mRoots负责存储所有Window所对应的ViewRootImpl,mParams负责存储所有Window所对应的参数,mDyingView负责存储已经被移除的View,或者说是调用了removeView的方法但是删除操作还未完成的Window对象。 在addView中通过如下方式将Window的一系列对象添加到列表中
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
- 通过ViewRootImpl来更新界面并完成Window的添加过程
这个过程是在WindowManagerGlobal.addView方法中完成的在addView方法中调用了ViewRootImpl的setView方法,在setView方法的内部会通过requestLayout完成异步刷新请求
//ViewRootImpl#requestLayout
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
接着会通过WindowSession最终完成Window的添加过程,mWindowSession的类型是IWindowSession,它是一个Binder对象,真正的实现类是Session,也就是说Window的添加过程试一次IPC的调用
//ViewRootImpl#setView
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
adjustLayoutParamsForCompatibility(mWindowAttributes);
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
setFrame(mTmpFrame);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
inputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
}
在Session内部会通过WindowManagerService来实现Window的添加,这样一来Window的添加过程就交给WindowManagerService去处理了,在WindowManagerService内部会为每一个应用保留一个单独的Session。
2.Window的删除过程
Window的删除过程也是由WindowManagerGlobal完成的,调用的是removeView方法
//WindowManagerGlobal#removeView
@UnsupportedAppUsage
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);
}
}
在removeView方法中先通过findViewLocked来查找待删除的索引,这个查找过程就是建立的数组遍历,数据来源就是之前创建的mViews,然后调用removeViewLocked来做进一步的删除
//WindowManagerGlobal#removeViewLocked
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (root != null) {
root.getImeFocusController().onWindowDismissed();
}
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
removeViewLocked方法是通过ViewRootImpl来完成删除操作的。在WindowManager中提供了两个删除方法removeView和removeViewImmediate,分别是异步删除和同步删除,一般来说removeViewImmediate不常用避免发生意外的错误,removeView的删除是异步删除,这个删除主要依靠的是ViewRootImpl的die方法来完成
//ViewRootImpl#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()方法。异步删除的View并没有立即完成删除操作,所以最后会将其添加搭配mDyingViews列表中,这个列表表示待删除的VIew列表。
真正删除View的逻辑是在dispatchDetachedFromWindow方法中实现,这个方法主要做了四件事情:
- 垃圾回收相关的工作,比如清除数据和消息、移除回调
- 通过Session的remove方法删除Window,这依旧是一个IPC的调用,最终调用的是WindowManagerService.removeWindow方法。
- 调用View的dispatchDetachedFromWindow方法,在内部调用View的onDetachedFromWindow()以及onDetachedFromWindowInternal()。当View从Window中移除时就会调用onDetachedFromWindow()方法,主要作用就是做一些资源回收的工作,比如终止动画停止线程等。
- 调用WindowManagerGlobal的doRemoveView方法刷新数据,需要将Window从mView、mParams、mDyingView列表中删除。
3.Window的更新过程
Window的更新过程还是通过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);
}
}
updateViewLayout方法中首先是跟新View的LayoutParams,然后更新ViewRootImpl的LayoutParams,在ViewRootImpl的setLayoutParams会通过scheduleTraversals方法对View重新布局,包括测量、布局、重绘三个过程,除了View的重绘之外ViewRootImpl还会通过WindowSession来更新Window的视图,这个过程最终是由WindowManagerService的relayoutWindow()来具体实现,这也是一个IPC过程。
本文转自 [https://juejin.cn/post/7181067713182171195],如有侵权,请联系删除。