注册

为什么onMeasure会被执行两次

什么情况下会onMeasure会执行?


进入Viewmeasure方法:


void measure(){
boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
boolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec
|| heightMeasureSpec != mOldHeightMeasureSpec;
boolean isSepcExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY
&& MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;
boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec)
&& getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);
final boolean needsLayout = specChanged
&& (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize);
if(forceLayout || needLayout){
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
if (cacheIndex < 0 || sIgnoreMeasureCache) {
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} else {
long value = mMeasureCache.valueAt(cacheIndex);
setMeasuredDimensionRaw((int) (value >> 32), (int) value);
mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
}
}

什么时候forceLayout=true:



  1. 调用requestLayout
  2. 调用forceRequestLayout

什么时候needsLayout=true:



  1. 当长宽发生改变

什么时候调用了onMeasure>方法:



  1. forceLayouy=true
  2. 或者mMeasureCache没有当前的缓存

所以总结:当调用了requestLayout一定会测发重测过程.当forceLayout=false的时候会去判断mMeasureCache值.现在研究下这个mMeasureCache


class View{
LongSparseLongArray mMeasureCache;
void measure(widthSpec,heightSpec){
---
long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
if(cacheIndex<0){
onMeasure(widthSpec,heightSpec);
}

mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
mMeasureCache.put(key,widhSpec|heightSpec);
---
}
}

这里可以看到oldWidthMeasureSpecmMeasureCache都是缓存上一次的值,那他们有什么不同呢?不同点就是,oldWidthMeasureSpec>不仅仅缓存了测量的spec模式而且缓存了size.但是mMeasureCache只缓存了size.从这行代码可以看出:


long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;

这里一同运算就为了排除掉spec造成的影响.


//不信你可以试下下面的代码
public class Test {
public static void main(String[] args) {
long widthMeasureSpec = makeMeasureSpec(10,0);
long heightMeasureSpec = makeMeasureSpec(20,0);
long ss = widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
System.out.println("=========="+ss);
}

private static final int MODE_MASK = 0x3 << 30;

public static int makeMeasureSpec(int size,
int mode) {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
//42949672980
//42949672980
//42949672980



什么时候mPrivateFlags会被赋值PFLAG_FORCE_LAYOUT.


view viewGrouup的构造函数里面会主动赋值一次,然后在ViewGroup.addView时候会给当前ViewmProvateFlags赋值PFLAG_FORCE_LAYOUT.




为什么onMeasure会被执行两次?


void measure(int widthMeasureSpec,int heightMeasureSpec){
----
boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
if(forceLayout | needsLayout){
onMeasure()
}
----
}
public void layout(int l, int t, int r, int b){
---
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
---
}

在第一次触发到measure方法时,forceLayoyt=true needsLayout=true,但是layout方法还没触发到.

在第二次触发到measure>方法时,forceLayout=true needsLayout=false,所以还是会进入onMeasure方法.这次会执行layout方法.然后我们在下次的时候forceLayout就等于false了.上面的这一段分析是分析的measure内部如何防止多次调用onMeasure.


现在分析外部是如何多次调用measure方法的:

Activity执行到onResume生命周期的时候,会执行WindowManager.addView操作,WindowManager的具体实现类是WindowManagerImpl然后addView操作交给了代理类WindowManagerGlobal,然后在WindowManagerGlobaladdView里面执行了ViewRootImpl.setView操作(ViewRootImpl对象也是在这个时候创建的),在ViewRootImpl会主动调用一次requestLayout,也就开启了第一次的视图 测量 布局 绘制.


setView的时候主动调用了一次ViewRootImpl.requestLayout,注意这个requestLayoutViewRootImpl的内部方法,和view viewGroup那些requestLayout不一样.在ViewRootImpl.requestLayout内部调用了performTraversals方法:


class ViewRootImpl{
void performTraversals(){
if(layoutResuested){
//标记1
windowSizeMayChanged |= measureHierarchy(host,lp,res,desiredWindowWidth,desiredWindowHeight);
}
//标记2
performMeasure()
performLayout()
}
void measureHierarchy(){
performMeasure()
}
}

ViewRootImpl的执行逻辑你可以看出,在执行performLayout之前,他自己就已经调用了两次performMeasure方法.所以你现在就知道为啥了.


作者:Librity
链接:https://juejin.cn/post/7197116653195624508
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册