Why onMeasure is executed twice

Under what circumstances will onMeasure be executed?

Enter the measure method of View:

void measure(){<!-- -->
    boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
    boolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec
        || heightMeasureSpec != mOldHeightMeasureSpec;
    boolean isSepcExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY
         & amp; & amp; MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;
    boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec. getSize(widthMeasureSpec)
         & amp; & amp; getMeasuredHeight() == MeasureSpec. getSize(heightMeasureSpec);
    final boolean needsLayout = specChanged
         & amp; & amp; (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;
        }
   }
}

When forceLayout=true:

  1. Call requestLayout
  2. Call forceRequestLayout

When needsLayout=true:

  1. When the length and width change

When is the onMeasure> method called:

  1. forceLayouy=true
  2. Or mMeasureCache has no current cache

So to sum up: when requestLayout is called, the retest process will be tested. When forceLayout=false, it will judge the value of mMeasureCache. Now research Download this 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, widthSpec|heightSpec);
        ---
    }
}

Here you can see that oldWidthMeasureSpec and mMeasureCache both cache the last value, so what is the difference between them? The difference is that oldWidthMeasureSpec> not only Only spec mode of measurement is cached and size is cached. But mMeasureCache only caches size. From this line The code can be seen:

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

The operation here is to eliminate the influence caused by spec.

//If you don't believe me, you can try the following code
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 & amp; ~MODE_MASK) | (mode & amp; MODE_MASK);
    }
}
//42949672980
//42949672980
//42949672980

When mPrivateFlags will be assigned PFLAG_FORCE_LAYOUT.

In the constructor of view viewGrouup, it will actively assign a value once, and then it will give mProvateFlags of the current View when ViewGroup.addView code>assign PFLAG_FORCE_LAYOUT.

Why is onMeasure executed twice?

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;
    ---
}

When the measure method is triggered for the first time, forceLayoyt=true needsLayout=true, but the layout method has not yet been triggered.
When the measure> method is triggered for the second time, forceLayout=true needsLayout=false, so it will still enter the onMeasure method. This time it will be executed layout method. Then we will forceLayout be equal to false next time. The above analysis is an analysis of measure How to prevent multiple calls to onMeasure.

Now analyze how the measure method is called multiple times externally:
When Activity executes to the onResume life cycle, it will execute the WindowManager.addView operation, the specific implementation class of WindowManager It is WindowManagerImpl and then the addView operation is handed over to the proxy class WindowManagerGlobal, and then addView of WindowManagerGlobal The ViewRootImpl.setView operation is executed in code> (the ViewRootImpl object is also created at this time), and ViewRootImpl will actively call once requestLayout, which starts the first time view measurement layout drawing.

In setView, ViewRootImpl.requestLayout is actively called once. Note that this requestLayout is the internal method of ViewRootImpl, and view viewGroup and requestLayout are different. The performTraversals method is called inside ViewRootImpl.requestLayout:

class ViewRootImpl{<!-- -->
    void performTraversals(){<!-- -->
        if(layoutResuested){<!-- -->
        // mark 1
            windowSizeMayChanged |= measureHierarchy(host,lp,res,desiredWindowWidth,desiredWindowHeight);
        }
        // mark 2
        performMeasure()
        performLayout()
    }
    void measureHierarchy(){<!-- -->
        performMeasure()
    }
}

From the execution logic of ViewRootImpl, you can see that before executing performLayout, he has called the performMeasure method twice. So you now Just know why.