第八章(2)---Window的内部机制

Window的内部机制

Window是一个抽象的概念,每一个Window都对应着一个View和一个ViewRootImplWindowView通过ViewRootImpl来建立联系,因此Window并不是实际存在的,它是以View的形式存在。这点从WindowManager的定义也可以看出,他提供的三个接口方法addViewupdateViewLayout以及removeView都是针对View的,这说明View才是Window存在的实体。在实际使用中无法直接访问Window,对Window的访问必须通过WindowManager。为了分析Window的内部机制,这里从Window的添加,删除以及更新说起。

8.2.1 Window的添加过程

Window的添加过程需要通过WindowManageraddView来实现,WindowManager是一个接口,它的真正实现是WindowManagerImpl类。在WindowManagerImplWindow的三大操作的实现如下:

@Override
public void addView(View view, ViewGroup.LayoutParams params) {
    mGlobal.addView(view, params, mDisplay, mParentWindow);
}

@Override
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
    mGlobal.updateViewLayout(view, params);
}

@Override
public void removeView(View view) {
    mGlobal.removeView(view, false);
}

可以发现,WindowManagerImpl并没有直接实现Window的三大操作,而是全部交给了WindowManagerGlobal来处理,WindowManagerGlobal以工厂的形式向外提供自己的实例,在WindowManagerGlobal中如下一段代码:

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

WindowManagerImpl这种工作模式是典型的桥接模式,将所有的操作全部委托给WindowManagerGlobal来实现。

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 mViews = new ArrayList();
private final ArrayList mRoots = new ArrayList();
private final ArrayList mParams =
        new ArrayList();
private final ArraySet mDyingViews = new ArraySet();

在上面的声明中,mViews存储的是所有Window所对应的ViewmRoots存储的是所有Window所对应的ViewRootImplmParams存储的是所有Window所对应的布局参数,而mDyingViews则存储了那些正在被删除的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的添加过程

这个步骤由ViewRootImplsetView方法来完成,从第四章可以知道,View的绘制过程是由ViewRootImpl来完成的,这里当然也不例外,在setView内部会通过requestLayout来完成异步刷新请求。在下面的代码中,scheduleTraversals实际是View的绘制入口:

@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

接着会通过mWindowSession最终来完成Window的添加过程。mWindowSession的类型是IWindowSession,它是一个Binder对象,真正的实现类是Session,也就是Window的添加过程是一次IPC调用。

没找到代码

Session内部会通过WindowManagerService来实现Window的添加。

没找到代码

如此一来,Window的添加请求就交给WindowManagerService去处理了,在WindowManagerService内部会为每一个应用保留一个单独的Session

8.2.2 Window的删除过程

Window的删除过程和添加过程一样,都是先通过WindowManagerImpl后,再进一步通过WindowManagerGlobal来实现的。下面是WindowManagerGlobalremoveView的实现:

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来查找待删除的View的索引,这个查找过程就是建立的数组遍历,然后再调用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);
         }
     }
 }

removeViewLocked是通过ViewRootImpl来完成删除操作的。在WindowManager中提供了两种删除接口removeViewremoveViewImmediate,它们分别表示异步删除同步删除,其中removeViewImmediate使用起来需要特别注意,一般来说不需要使用此方法来删除Window以免发生意外的错误。这里主要说异步删除的情况,具体的删除操作由ViewRootImpldie方法来完成。在异步删除的情况下,die方法只是发送了一个请求删除的消息后就立刻返回了,这个时候View并没有完成删除操作,所以最后会将其添加到mDyingViews中,mDyingViews表示待删除的View列表。ViewRootImpldie方法如下所示:

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(TAG, "Attempting to destroy the window while drawing!\n" +
                 "  window=" + this + ", title=" + mWindowAttributes.getTitle());
     }
     mHandler.sendEmptyMessage(MSG_DIE);
     return true;
 }

die方法内部只是做了简单的判断,如果是异步删除,那么就发送一个MSG_DIE的消息,ViewRootImpl中的Handler会处理此消息并调用doDie方法,如果是同步删除(立即删除),那么就不发消息直接调用doDie方法,这就是这两种删除方式的区别。在doDie内部会调用dispatchDetachedFromWindow方法,真正删除View的逻辑在dispatchDetachedFromWindow方法的内部实现。dispatchDetachedFromWindow方法主要做四件事:

  • 垃圾回收相关的工作,比如清除数据和消息,移除回调。
  • 通过Session的remove方法删除Window:mWindowSession.remove(mWindow),这同样是一个IPC过程,最终会调用WindowManagerServiceremoveWindow方法。
  • 调用ViewdispatchDetachedFromWindow,在内部会调用ViewonDetachedFromWindow()以及onDetachedFromWindowInternal()。对于onDetachedFromWindow()大家一定不陌生,当ViewWindow中移除时,这个方法就会被调用,可以在这个方法的内部,做一些资源回收工作,比如终止动画,停止线程等。
  • 调用WindowManagerGlobaldoRemoveView方法刷新数据,包括mRootsmParams以及mDyingViews,需要将当前Window所关联的这三类对象从列表中删除。
8.2.3 Window的更新过程

下面分析Window的更新过程,还是要看WindowManagerGlobalupdateViewLayout方法。

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);
    }
}

首先它需要更新ViewLayoutParams并替换掉老的LayoutParams,接着在更新ViewRootImpl中的LayoutParams,这一步是通过ViewRootImplsetLayoutParams方法来实现的。在ViewRootImpl中会通过scheduleTraversals方法来对View重新布局,包括测量,布局,重绘这三个过程。除了View本身的重绘以外,ViewRootImpl还会通过WindowSession来更新Window的视图,这个过程最终是由WindowManagerServicerelayoutWindow()来具体实现的,它同样是一个IPC过程。

你可能感兴趣的:(第八章(2)---Window的内部机制)