Window的创建,更新,删除

 · 1 min read

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的更新过程

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来具体实现