一 概述
ViewRootImpl 是一个视图层次结构的顶部,可以理解为一个 Window 中所有 View 的根 View 的管理者(但 ViewRootImpl 不是 View,只是实现了 ViewParent 接口),实现了 View 和 WindowManager 之间的通信协议,实现的具体细节在 WindowManagerGlobal 这个类中。
简单来说 ViewRootImpl 是 View 与 WindowManager 之间联系的桥梁,作用总结如下:
1.将 DecorView 传递给 WindowManagerSerive
2.完成 View 的绘制过程,包括 measure、layout、draw 过程
3.向 DecorView 分发收到的用户发起的 event 事件,如按键,触屏等事件。
其中,ViewRootImpl 中包含了两个需要重点关注的内部类:
- final class ViewRootHandler extends Handler 用于向 DecorView 分发事件
- static class W extends IWindow.Stub
W 是 ViewRootImp l的一个嵌入类,也是一个 Binder 服务。通过 mWindowSession.addToDisplay 函数传入 WMS,用来在 WMS 中通过 Binder 回调。
二 将Window传递给WMS
public void setView(View view, WindowManager.LayoutParams attrs,
View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
......
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();//见下节介绍
if ((mWindowAttributes.inputFeatures
& WindowManager.
LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();//生成InputChannel
}
......
try {
......
//调用mWindowSession.addToDisplay通过binder调用到WMS
//实现对Window的真正的添加,这里的mWindow为 W 对象
res = mWindowSession.addToDisplay(mWindow, mSeq, ......);
setFrame(mTmpFrame);
} catch (RemoteException e) {
......
} finally {
......
}
......
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
//用于输入事件的接收
mInputEventReceiver =
new WindowInputEventReceiver(
mInputChannel, Looper.myLooper());
}
......
}
}
}
三 View的绘制
3.1 requestLayout
frameworks/base/core/java/android/view/View.java
public void requestLayout() {
if (mMeasureCache != null) mMeasureCache.clear();
// 步骤1
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null && viewRoot.isInLayout()) {
if (!viewRoot.requestLayoutDuringLayout(this)) {
return;
}
}
mAttachInfo.mViewRequestingLayout = this;
}
// 步骤2
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
调用时机:
当某些变化导致原有 View 树的 layout 发生变化,可以调用 requestLayout 重新布局 View 树。
1.关于 requestLayout 的叠加
- 尽量不要在 layout 过程中又调用 requestLayout
- 如果非要叠加调用 requestLayout,则将当前作出该请求的 View 先放入 mLayoutRequesters 列表,等到调用 ViewRootImpl#performLayout 的时候再取出 mLayoutRequesters 列表依次调用各个 View#requestLayout。如果当前正在处理 mLayoutRequesters 列表,则不要再次触发 requestLayout
2.mParent.requestLayout()
-
我们先搞清楚 mParent 是啥?
mParent 的类型是 ViewParent,通过代码溯源 View#assignParent,可以得出如下结论:
DecorView (即根 View) 对应的 mParent 是 ViewRootImpl,普通子 View (非根 View) 对应的 mParent 是子 View 的父 View (即 ViewGroup) -
requestLayout 调用过程?
该过程是一个从子 View -> 父 View -> DecorView -> ViewRootImpl 层层往上的递归调用过程
3.2 invalidate
frameworks/base/core/java/android/view/View.java
public void invalidate() {
invalidate(true);
}
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft,
mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b,
boolean invalidateCache, boolean fullInvalidate) {
if (skipInvalidate()) {
return;
}
......
// Propagate the damage rectangle to the parent view.
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
p.invalidateChild(this, damage);
}
......
}
frameworks/base/core/java/android/view/ViewGroup.java
@Override
public final void invalidateChild(View child, final Rect dirty) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null && attachInfo.mHardwareAccelerated) {
// HW accelerated fast path
onDescendantInvalidated(child, child);
return;
}
ViewParent parent = this;
if (attachInfo != null) {
final int[] location = attachInfo.mInvalidateChildLocation;
location[CHILD_LEFT_INDEX] = child.mLeft;
location[CHILD_TOP_INDEX] = child.mTop;
......
do {
......
parent = parent.invalidateChildInParent(location, dirty);
......
} while (parent != null);
}
}
1.调用时机:
当前 View 树需要重绘时。如果当前 View 可见,则会调到 onDraw 方法。
该方法必须在 UI 线程调用。
2.View#invalidate 的调用逻辑:
从子 View -> 父 View -> DecorView -> ViewRootImpl 从子到父层层调用。
-
硬件加速
ViewGroup#onDescendantInvalidated -> …-> DecorView#onDescendantInvalidated -> ViewRootImpl#onDescendantInvalidated -
非硬件加速
ViewGroup#invalidateChild -> ViewGroup#invalidateChildInParent -> … -> DecorView#invalidateChildInParent -> ViewRootImpl#invalidateChildInParent
3.skipInvalidate 逻辑
private boolean skipInvalidate() {
return (mViewFlags & VISIBILITY_MASK) != VISIBLE &&
mCurrentAnimation == null &&
(!(mParent instanceof ViewGroup) ||
!((ViewGroup) mParent).isViewTransitioning(this));
}
如果当前 View 不可见并且当前没有动画时,则不会 invalidate 执行重绘.
3.3 postInvalidate
frameworks/base/core/java/android/view/View.java
public void postInvalidate() {
postInvalidateDelayed(0);
}
public void postInvalidateDelayed(long delayMilliseconds) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
attachInfo.mViewRootImpl.dispatchInvalidateDelayed(
this, delayMilliseconds);
}
}