WindowManager的定义
在实际使用中无法直接访问Window,对Window的访问必须通过WindowManager。
WindowManager是一个接口,它继承于ViewManager接口,ViewManager的定义如下:1
2
3
4
5
6public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
这三个方法都是对View进行操作。这说明View才是Window存在的实体。
为了分析Window的内部机制,这里就从这三个方法开始,分别分析Window的添加、更新和删除过程。
Window的添加过程
WindowManagerImpl#addView
WindowManager是一个接口,它的真正实现是WindowManagerImpl。在WindowManagerImpl中三个操作的实现是:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
public void removeView(View view) {
mGlobal.removeView(view, false);
}
其中mGlobal是一个WindowManagerGlobal实例:1
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
所以WindowManagerImpl并没有实现这三个操作,而是交由WindowManagerGlobal来实现,WindowManager的这种工作模式是典型的桥接模式,将所有的操作委托给WindowManagerGlobal来实现。
WindowManagerGlobal#addView
1 | public void addView(View view, ViewGroup.LayoutParams params, |
其中,WindowManagerGlobal的几个重要实例变量如下:1
2
3
4
5
6
7
8
9//mViews存储的是所有Window所对应的View
private final ArrayList<View> mViews = new ArrayList<View>();
//mRoots存储的是所有Window所对应的ViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
//mParams存储的是所有Window所对应的布局参数
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
//mDyingViews存储的是那些正在被删除的View对象
private final ArraySet<View> mDyingViews = new ArraySet<View>();
创建完ViewRootImpl并将View添加至列表后,将会调用ViewRootImpl的setView方法更新界面
ViewRootImpl#setView
1 | public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { |
在该方法中,首先调用requestLayout方法完成异步刷新,接着调用Session的addToDisplay方法最终完成Window的添加,这是一个IPC的调用。
Session#addToDisplay
1 |
|
可以看到,最终是通过WindowManagerService的addWindow方法完成Window的添加
小结
Window的添加从WindowManagerImpl的addView方法开始,但WindowManagerImpl并没有做什么,而是交由WindowManagerGlobal来实现。WindowManagerGlobal先创建ViewRootImpl,并调用ViewRootImpl的setView方法更新界面。在更新界面时,首先调用requestLayout方法完成异步刷新,之后进行一次IPC调用,最终通过WindowManagerService的addWindow方法完成Window的添加。
Window的更新过程
WindowManagerGlobal#updateViewLayout
1 | public void updateViewLayout(View view, ViewGroup.LayoutParams params) { |
在该方法中,首先更新View的LayoutParams,接着更新ViewRootImpl的LayoutParams,ViewRootImpl会通过scheduleTraversals方法让View重新开始三大工作流程,并调用Session的relayout方法更新Window,最终通过WindowManagerService的relayoutWindow方法更新Window。
Window的删除过程
WindowManagerGlobal#removeView
1 | public void removeView(View view, boolean immediate) { |
调用removeViewLocked方法做进一步的删除
WindowManagerGlobal#removeViewLocked
1 | private void removeViewLocked(int index, boolean immediate) { |
通过ViewRootImpl的die方法来完成删除工作
ViewRootImpl#die
1 | boolean die(boolean immediate) { |
在die方法中,判断是同步删除还是异步删除。同步删除直接调用doDie方法,异步删除则先发送一条消息,ViewRootImpl的Handler收到此消息后再执行doDie方法。
ViewRootImpl#doDie
1 | void doDie() { |
真正删除View的逻辑在dispatchDetachedFromWindow方法中
ViewRootImpl#dispatchDetachedFromWindow
1 | void dispatchDetachedFromWindow() { |
该方法主要做了这几件事:
- 调用View的dispatchDetachedFromWindow方法,该方法会调用onDetachedFromWindow方法,我们可以在这个方法的内部做一些资源回收工作,例如终止动画和线程。
- 进行回收工作:清除数据和消息、移除回调等。
- 通过Session的remove方法删除Window,并最终调用WindowManagerService的removeWindow方法来删除Window。
小结
WindowManagerGlobal将删除操作交给ViewRootImpl,ViewRootImpl在删除的时候先判断是同步删除还是异步删除。同步删除直接调用doDie方法,异步删除则先发送一条消息,ViewRootImpl的Handler收到此消息后再执行doDie方法。最终,ViewRootImpl在删除时主要做了这几件事:
- 回调View的onDetachedFromWindow方法,我们可以重写该方法,并在该方法做一些资源回收工作,例如终止动画和线程。
- 进行回收工作:清除数据和消息、移除回调等。
- 跨进程调用WindowManagerService的removeWindow方法最终删除Window。
参考
- 《Android 开发艺术探索》