源码分析:Window的内部机制

WindowManager的定义

在实际使用中无法直接访问Window,对Window的访问必须通过WindowManager。

WindowManager是一个接口,它继承于ViewManager接口,ViewManager的定义如下:

1
2
3
4
5
6
public 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
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

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

@Override
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
//一些参数的检查

//如果是子View的话,需要调整一些布局参数
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} //...


ViewRootImpl root;
View panelParentView = null;

synchronized (mLock) {
//...

//创建ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);

view.setLayoutParams(wparams);
//将View添加至列表
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);

//更新界面
root.setView(view, wparams, panelParentView);
//...
}
}

其中,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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;

//...

//完成异步刷新请求
requestLayout();

//...

try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();

//mWindowSession是一个IWindowSession对象,
//IWindowSession是一个AIDL接口,真正的实现类是Session
//所以这里是调用Session的addToDisplay方法
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}

//...
}
}
}

在该方法中,首先调用requestLayout方法完成异步刷新,接着调用Session的addToDisplay方法最终完成Window的添加,这是一个IPC的调用。

Session#addToDisplay

1
2
3
4
5
6
7
8
9
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {

//mService是一个WindowManagerService对象
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}

可以看到,最终是通过WindowManagerService的addWindow方法完成Window的添加

小结

Window的添加从WindowManagerImpl的addView方法开始,但WindowManagerImpl并没有做什么,而是交由WindowManagerGlobal来实现。WindowManagerGlobal先创建ViewRootImpl,并调用ViewRootImpl的setView方法更新界面。在更新界面时,首先调用requestLayout方法完成异步刷新,之后进行一次IPC调用,最终通过WindowManagerService的addWindow方法完成Window的添加。

Window的更新过程

WindowManagerGlobal#updateViewLayout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  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的LayoutParams
view.setLayoutParams(wparams);

synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
//更新ViewRootImpl的LayoutParams
root.setLayoutParams(wparams, false);
}
}

在该方法中,首先更新View的LayoutParams,接着更新ViewRootImpl的LayoutParams,ViewRootImpl会通过scheduleTraversals方法让View重新开始三大工作流程,并调用Session的relayout方法更新Window,最终通过WindowManagerService的relayoutWindow方法更新Window。

Window的删除过程

WindowManagerGlobal#removeView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 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);
}
}

调用removeViewLocked方法做进一步的删除

WindowManagerGlobal#removeViewLocked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();

//...

//通过ViewRootImpl来完成删除工作
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
//将该View添加至待删除列表
mDyingViews.add(view);
}
}
}

通过ViewRootImpl的die方法来完成删除工作

ViewRootImpl#die

1
2
3
4
5
6
7
8
9
10
11
12
13
14
boolean die(boolean immediate) {

//同步删除,直接调用doDie方法
if (immediate && !mIsInTraversal) {
doDie();
return false;
}

//...

//异步删除,发送消息,ViewRootImpl的Handler收到此消息后执行doDie方法
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}

在die方法中,判断是同步删除还是异步删除。同步删除直接调用doDie方法,异步删除则先发送一条消息,ViewRootImpl的Handler收到此消息后再执行doDie方法。

ViewRootImpl#doDie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void doDie() {
checkThread();

synchronized (this) {
//...

if (mAdded) {
//在该方法中真正删除View
dispatchDetachedFromWindow();
}

//...
}
WindowManagerGlobal.getInstance().doRemoveView(this);
}

真正删除View的逻辑在dispatchDetachedFromWindow方法中

ViewRootImpl#dispatchDetachedFromWindow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void dispatchDetachedFromWindow() {

if (mView != null && mView.mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);

//在该方法中会调用onDetachedFromWindow方法
mView.dispatchDetachedFromWindow();
}

//回收工作:清除数据和消息、异常回调等

try {
//通过Session的remove方法删除Window
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}



}

该方法主要做了这几件事:

  1. 调用View的dispatchDetachedFromWindow方法,该方法会调用onDetachedFromWindow方法,我们可以在这个方法的内部做一些资源回收工作,例如终止动画和线程。
  2. 进行回收工作:清除数据和消息、移除回调等。
  3. 通过Session的remove方法删除Window,并最终调用WindowManagerService的removeWindow方法来删除Window。

小结

WindowManagerGlobal将删除操作交给ViewRootImpl,ViewRootImpl在删除的时候先判断是同步删除还是异步删除。同步删除直接调用doDie方法,异步删除则先发送一条消息,ViewRootImpl的Handler收到此消息后再执行doDie方法。最终,ViewRootImpl在删除时主要做了这几件事:

  1. 回调View的onDetachedFromWindow方法,我们可以重写该方法,并在该方法做一些资源回收工作,例如终止动画和线程。
  2. 进行回收工作:清除数据和消息、移除回调等。
  3. 跨进程调用WindowManagerService的removeWindow方法最终删除Window。

参考

  • 《Android 开发艺术探索》
-------------    本文到此结束  感谢您的阅读    -------------
0%