什么情况下会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办法还没触发到.
在第2次触发到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办法.所以你现在就知道为啥了.