Window是一个抽象类,它的真正实现是PhoneWindow。所以我们知道Window并不是真正存在的,它是以View的形式存在。对Window的访问必须通过WindowManager.
Window的添加过程
Window的添加过程是通过WindowManager的addView来实现的,而WindowManager是一个接口,它的真正实现是WindowManagerImpl类。
@Override
public void addView(View view, ViewGroup.LayoutParams params){
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
@Override
public void updateView(View view, ViewGroup.LayoutParams params){
mGlobal.updateView(view, params);
}
@Override
public void removeView(View view){
mGlobal.removeView(view, false);
}
可以看到WindowManagerImpl并没有真正实现window的添加、更新、删除,而是交给了WindowManagerGlobal来处理,这种是典型的桥接模式。
WindowManagerGlobal的addView方法主要分为下面几步。 1.检查参数是否合法,如果是子View的话还要调整一下布局参数
if(view == null){
throw new IllegalArgumentException("view must not be null");
}
if(!(params isntanceof WindowManager.LayoutParams)){
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
if(diaplay == null){
throw new IllegalArgumentException("diaplay must not be null");
}
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存储的是所有Window所对应的ViewRootImpl
- mParams存储的是所有Window所对应的布局参数
- mDyingViews存储的是那些正在被删除的View对象
3.添加一系列参数到上面的列表中
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
4.通过ViewRootImpl来更新界面并完成Window的添加过程
这个步骤由ViewRootImpl的setView方法来完成,ViewRootImpl会完成View的绘制。在setView内部会通过requestLayout来完成异步刷新请求。
public void requestLayout(){
...
scheduleTraversals();//执行View的绘制
}
5.通过WindowSession来完成Window的添加过程
在下面代码中mWindowSession的类型是IWindowSession,是一个Binder对象,真正的实现类是Session
...
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mInputChannel);
...
在真正实现类Session内部会通过WindowManagerService来实现Window的添加
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParamsattrs, int viewVisibility, int displayId, Rect outContentInsets, InputChannel outInputChannel){
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outContentInsets, outInputChannel);
}
这样一来,window的添加请求就交给WindowManagerService去处理了。
Window的删除过程
Window的删除过程跟添加过程一样,先通过WindowManagerImpl,再通过WindowManagerGlobal来实现的,WindowManagerGlobal的removeView的代码如下
public void removeView(View view, boolean immidiate){
...
synchronized(mLock){
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
removeViewLocked(index, immidiate);
}
}
先通过findViewLocked找到要删除的view的索引 再通过removeViewLocked进行进一步的删除
private void removeViewLocked(int index, boolean immidiate){
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
boolean deferred = root.die(immidiate);
...
if(deferred){
mDyingViews.add(view);
}
}
root的die方法如下所示。首先View的删除有两种,异步删除和同步删除。immidiate为true就是同步删除,反之亦然。
boolean die(boolean immidiate){
if(immidiate && !mIsInTraversal){
doDie();
return false;
}
mHandler.sendEmptyMessage(MSG_DIE);
rerturn true;
}
如果是同步删除,则立即调用doDie()方法,如果是异步删除,则会发送一个MSG_DIE的消息,root中的Handler会处理这个消息并执行doDie()。
在doDie()内部会调用dispatchDetachedFromWindow方法,它的内部主要做了这几件事
- 垃圾回收工作,包括清除数据和信息,移除回调
- 通过mWindowSession.remove(mWindow),这是一个IPC过程,最终实现是Session的remove(mWindow),它的内部同样会调用WindowManagerService的removeWindow方法
- 调用View的dispatchDetachedFromWindow,最终会会调用onDeteachedFromWindow(),我们可以在这个方法中做一些资源回收工作
- 调用WindowManagerGlobal的doRemoveView方法,刷新mRoots,mParams,mDyingViews
Window的更新过程
Window的更新过程跟添加过程一样,先通过WindowManagerImpl,再通过WindowManagerGlobal来实现的,WindowManagerGlobal的updateViewLayout的代码如下
public void updateViewLayut(View view, ViewGroup.LayoutParams params){
...
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
view.setLayoutParams(params);
synchronized(mLock){
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
root.setLayoutParams(wparams, false);
}
}
首先它需要替换掉View的老的LayoutParams,接下来更新ViewRootImpl中的LayoutParams。在ViewRootImpl中还会对View进行重新布局,包括测量、布局、重绘。除了View本身的重绘以外,ViewRootImpl还会通过WindowSeesion来更新window的视图,最终会通过WindowManagerService的relayoutWindow来具体实现