ViewRootImpl 与 DecorView
ViewRootImpl是连接WindowManager和DecorView的纽带。
联系过程
Activity # makeVisible()
创建WindowManagerImpl对象并调用其addView()将DecorView传入。
void makeVisible() {
if (!mWindowAdded) {
// 返回WindowManagerImpl对象
ViewManager wm = getWindowManager();
// 传入DecorView,调用WindowManagerImpl的addView()
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
WindowManagerImpl # addView()
WindowManagerImpl的addView()其实调用了WindowManagerGlobal的addView()。
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
// 调用WindowManagerGlobal的addView()
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
WindowManagerGlobal # addView()
创建ViewRootImpl,并调用其setView()传入DecorView。
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// ......
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// ......
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
// 将相关加入WindowManagerGlobal的三个全局集合中
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
// 将DecorView与ViewRootImpl连接起来
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
ViewRootImpl # setView()
ViewRootImpl将DecorView赋值给全局变量mView,作为View链的根View。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
// 第一个setView的赋值给mView,表示根View
if (mView == null) {
mView = view;
// ......
}
}
}
总结
WindowManagerGlobal中持有若干个ViewRootImpl,每个ViewRootImpl都会管理一条View链中所有View,一个Window可能存在多个View链,所以可能存在多个ViewRootImpl。比如以DecorView为根的View链,在DecorView中增删改子View都会用到ViewRootImpl。
拓展
Android视图框架Activity,Window,View,ViewRootImpl理解
MeasureSpec
MeasureSpec是View的一个静态内部类。在View的测量过程中需要用到一个32位int型的量,高2位表示SpecMode,低30位表示SpecSize。
SpecMode分类
- UNSPECIFIED(00):父容器不对View做任何限制。
- EXACTLY(01):精确大小,测量大小为SpecSize。对应具体数值或match_parent。
- AT_MOST(10):父容器指定最大值。对应wrap_content。
MeasureSpec类源码
public static class MeasureSpec {
// mode需要的偏移量
private static final int MODE_SHIFT = 30;
// 11 000000000000000000000000000000
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
public @interface MeasureSpecMode {}
// 三种SpecMode对应的值
// 00 00...0
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
// 01 00...0
public static final int EXACTLY = 1 << MODE_SHIFT;
// 10 00...0
public static final int AT_MOST = 2 << MODE_SHIFT;
// 合并SpecMode和SpecSize生成MeasureSpec
public static int makeMeasureSpec(int size, int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
// 加判断UNSPECIFIED
public static int makeSafeMeasureSpec(int size, int mode) {
if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
return 0;
}
return makeMeasureSpec(size, mode);
}
// 从MeasureSpec中提取SpecMode
@MeasureSpecMode
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
// 从MeasureSpec中提取SpecMode
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
// 调整MeasureSpec中的SpecSize
static int adjust(int measureSpec, int delta) {
final int mode = getMode(measureSpec);
int size = getSize(measureSpec);
// UNSPECIFIED不需要调整
if (mode == UNSPECIFIED) {
return makeMeasureSpec(size, UNSPECIFIED);
}
// 调整SpecSize
size += delta;
if (size < 0) {
Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size +
") spec: " + toString(measureSpec) + " delta: " + delta);
size = 0;
}
// 重新打包
return makeMeasureSpec(size, mode);
}
public static String toString(int measureSpec) {
int mode = getMode(measureSpec);
int size = getSize(measureSpec);
StringBuilder sb = new StringBuilder("MeasureSpec: ");
if (mode == UNSPECIFIED)
sb.append("UNSPECIFIED ");
else if (mode == EXACTLY)
sb.append("EXACTLY ");
else if (mode == AT_MOST)
sb.append("AT_MOST ");
else
sb.append(mode).append(" ");
sb.append(size);
return sb.toString();
}
}
MeasureSpec的创建依据
- 对于DecorView的确定因素
- 窗口尺寸
- 子身的LayoutParams
- 对于普通View的确定因素
- 父布局的MeasureSpec
- 自身的LayoutParams
一个View的MeasureSpec确定后,View的宽高会在onMeasure()根据MeasureSpec来确定。
DecorView的MeasureSpec创建
View的绘制流程是从ViewRootImpl的performTraversals()开始的,其中会调用measureHierarchy()创建DecorView的MeasureSpec。
ViewRootImpl # measureHierarchy()
// ......
// desiredWindowWidth为窗口的宽度,lp.width为DecorView的layourParams中设置的宽度
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
// 得到DecorView宽高的MeasureSpec后调用performMeasure()去完成DecorView的测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// ......
ViewRootImpl # getRootMeasureSpec()
根据window尺寸和DecorView的LayourParams来决定DecorView的MeasureSpec。
// windowSize就是Window的大小,rootDimension指的是DecorView的layoutParams中设置的大小
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
// match_parent和fill_parent的值都是-1
case ViewGroup.LayoutParams.MATCH_PARENT: windowSize.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
// DecorView设置的具体数值
default:
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
ViewRootImpl # performMeasure()
传入创建好的MeasureSpec,调用View的measure()开始测量过程。
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
// 开始测量过程
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
总结
DecorView的MeasureSpec由自身LayoutParams和Window尺寸决定。
layoutParams\MeasureSpec | SpecSize | SpecMode |
---|---|---|
MATCH_PARENT | windowSize | EXACTLY |
WRAP_CONTENT | windowSize | AT_MOST |
具体数值 | rootDimension | EXACTLY |
普通View的MeasureSpec创建
普通View的measure()是由ViewGroup的measureChildWithMargins()调用的。
ViewGroup # measureChildWithMargins()
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
// 参数中的两个MeasureSpec是ViewGroup的MeasureSpec
// widthUsed和heightUsed已经占用的宽度和高度
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
// 传入ViewGroup的MeasureSpec、ViewGroup中不属于自己的大小、View的layoutParams中定义的大小。
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
// 计算出MeasureSpec后开始进行测量流程。
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
ViewGroup # getChildMeasureSpec()
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
// 父布局的Mode和Size
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
// 父布局原大小减去已用大小如果小于0,就取0
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// 如果父布局Mode是EXACTLY
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
// 自身设置的是具体数值
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// 自身设置的是match_parent
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// 自身设置的是wrap_content
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// 父布局Mode为AT_MOST
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// 自身设置的是具体数值
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// 自身设置的是match_parent
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// 自身设置的是wrap_content
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// 自身设置的是具体数值
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// 自身设置的是match_parent
// 如果设置了sUseZeroUnspecifiedMeasureSpec,大小就是0
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// 自身设置的是wrap_content
// 如果设置了sUseZeroUnspecifiedMeasureSpec,大小就是0
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
// 合并返回
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
总结
普通View的MeasureSpec由父布局的MeasureSpec和自身的LayoutParams决定。
自身lp\父布局mode | EXACTLY | AT_MOST | UNSPECIFIED |
---|---|---|---|
具体数值 | childSize EXACTLY | childSize EXACTLY | childSize EXACTLY |
match_parent | parentSize EXACTLY | parentSize AT_MOST | 0 or parentSize UNSPECIFIED |
wrap_content | parentSize AT_MOST | parentSize AT_MOST | 0 or parentSize UNSPECIFIED |