再看此篇文章前建议先看理解Window和WindowManager关系。
前言
我们知道Window是一个抽象类,真正用于展示的是View类,每个View对应ViewRootImpl类,ViewRootImpl职责非常多。WindowManager提供了3个方法添加、更新、删除都是针对View,说明Window的存在靠View来实现,对WIndow的访问需使用WIndowManager,接下来我们开始分析这三个方法具体实现。
2.1 Window 添加过程
当我们要显示一个Window时候,需要在WindowManager添加一个View,我们通过WindowManagerImpl类的addView来完成。首先我们看下WindowManagerImpl的添加源码,如下:
//1
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
//2
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
在注释1处通过单例的方式获取WindowManagerGlobal对象。注释2处WindowManagerImpl的addView方法添加View但并没有做事情,而是把添加的过程委托给WindowManagerGlobal实现。我们接着看注释1处获取单例的源码,如下。
public static WindowManagerGlobal getInstance() {
synchronized (WindowManagerGlobal.class) {
if (sDefaultWindowManager == null) {
sDefaultWindowManager = new WindowManagerGlobal();
}
return sDefaultWindowManager;
}
}
这是一个典型的单例模式,一个进程只有一个WindowManagerGlobal对象。接下来我们继续分析WindowManagerGlobal类的具体的添加View过程。
//1
@UnsupportedAppUsage
private final ArrayList<View> mViews = new ArrayList<View>();
//2
@UnsupportedAppUsage
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
//Window对应的布局参数
@UnsupportedAppUsage
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
//4
private final ArraySet<View> mDyingViews = new ArraySet<View>();
注释1处mView存储Window对应的View。注释2处mRoots存储Window对应的ViewRootImpl。注释3处mParams存储Window对应布局参数。注释4处存储那些正在被删除的View对象,调用了removeView方法,但是删除操作还未完成的Window对象。接下来我们继续分析addView源码,如下:
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
//1
(parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
//2
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//3
mViews.add(view);
//4
mRoots.add(root);
//5
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
//6
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
}
}
}
注释1处如果是子窗口,都会根据父窗口的LayoutParams类的wparams对象来调整子窗口的wparams。给应用程序Window和子Window的LayoutParams添加token,前者token都是Activity的token,后者token都是宿主窗口token。注释2处创建ViewRootImpl对象赋值给root。注释3处将窗口view添加到mView中。注释4处将root添加到mRoots中。注释5处将窗口参数添加到mParams中。注释6处将窗口和窗口参数传递到RootView中。
ViewRootImpl在其实做了相当多的事情,如下:
- View的树根和管理View树。
- 触发View测绘、布局、绘制。
- 输入事件的中转站。
- 管理surface。
- 负责与AMS通讯。
ViewRootImpl的职责非常多,现在继续分析ViewRootImpl的setView方法,在此方法里面会调用requestLayout方法来布局。
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
然后会调用scheduleTraversals方法来执行View测绘、布局、绘制,具体过程会在我下一篇文章ViewRootImpl理解源码分析。我们接着看setView源码,如下:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
requestLayout();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
}
}
通过调用mWindowSession的addDisplay方法实现了Window添加的过程,mWindowSession是IWindowSession类对象,也是binder类,真正的实现是Session类。
2.2Window 更新过程
要了解WindowManager的更新过程,接着我们继续看WindowManageImpl的updateViewLayout方法,代码如下:
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
WindowManageImpl的updateViewLayout实际没有做什么事情,具体的实现都委托给了WindowManagerGlobal来实现,那我都继续往下面走。
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
//1
view.setLayoutParams(wparams);
synchronized (mLock) {
//2
int index = findViewLocked(view, true);
//3
ViewRootImpl root = mRoots.get(index);
//4
mParams.remove(index);
mParams.add(index, wparams);
//5
root.setLayoutParams(wparams, false);
}
}
注释1处对View赋值新的布局属性。注释2处在mViews数组遍历view对象的索引位置。注释3处根据索引找出对应窗体View的ViewRootImpl对象。注释4处移出之前之前窗体布局参数,添加新的布局参数。注释5处重新设置新的布局参数。我们继续往下分析ViewRootImpl的setLayoutParams具体做了些什么事情,代码如下:
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
synchronized (this) {
scheduleTraversals();
}
}
从代码看出此方法的实现很简单,调用scheduleTraversals方法来执行View测绘、布局、绘制,然后调用了IWindowSession的relayout方法,最后又调用了WindowManagerService的relayoutWindow方法实现了Window的更新,这两步过程进行了跨进程调用。
2.3 Window删除过程
我们先看下WindowManager的remove方法,源码如下:
public void removeView(View view) {
mGlobal.removeView(view, false);
}
WindowManageImpl的removeView方法实际没有做什么事情,具体的实现都委托给了WindowManagerGlobal来实现,那我都继续往下面走。
public void removeView(View view, boolean immediate) {
synchronized (mLock) {
//1
int index = findViewLocked(view, true);
//2
View curView = mRoots.get(index).getView();
//3
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
}
}
注释1处在mViews数组找到对应窗体view的索引。注释2处找到对应索引的ViewRootImpl的对象拿出对应窗体View来。注释3处removeViewLocked方法根据索引移出窗体view。继续分析removeViewLocked方法的具体实现方式,源码如下:
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (view != null) {
InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class);
if (imm != null) {
//1
imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
//2
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
//3
mDyingViews.add(view);
}
}
}
注释1处结束view的输入相关逻辑。注释2处执行相应的删除操作,immediate表示是否立即删除。注释3处存储待删除的view,立刻删除不会存入此数组中。我们接着分析注释2处ViewRoot的die源码,如下:
boolean die(boolean immediate) {
//1
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
//2
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
注释1处立即执行删除操作。注释2处发送消息执行删除操作。继续看doDie方法做了些什么事情,代码如下:
void doDie() {
//1
checkThread();
if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
synchronized (this) {
if (mRemoved) {
return;
}
mRemoved = true;
if (mAdded) {
//2
dispatchDetachedFromWindow();
}
if (mAdded && !mFirst) {
destroyHardwareRenderer();
if (mView != null) {
int viewVisibility = mView.getVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
if (mWindowAttributesChanged || viewVisibilityChanged) {
// If layout params have been changed, first give them
// to the window manager to make sure it has the correct
// animation info.
try {
if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
& WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
mWindowSession.finishDrawing(mWindow);
}
} catch (RemoteException e) {
}
}
destroySurface();
}
}
mAdded = false;
}
//3
WindowManagerGlobal.getInstance().doRemoveView(this);
}
注释1调用checkThread方法检测是否在主线程操作。注释2处移出view,移出监听回调,移出绘制消息,数据重置,最终会调用onDetachedFromWindow方法,可以继承view的此方法拿到回调 ,会调用mWindowSession的remove方法移出window对象,是一个IPC过程。注释3处调用WindowManagerGlobal的doRemoveView方法删除具体的存在的mView、mRoot、mParams数组数据。