From 93c567cddc9354b33f66cb62d514386ac2e052b0 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Tue, 16 Jul 2013 15:10:04 -0700 Subject: Skip unnecessary measurements when possible This change introduces a new measure cache to View, to remember the measured dimensions for previous pairs of measure specs. The measure cache is cleared whenever a View requests layout. Unfortunately some Views rely on measure being always called when layout is invoked. To work around this problem, we need to remember when we hit the measure cache to force a call to measure just prior to calling onLayout(). This does not completely removes all measure calls but enough to optimize a number of layouts. Change-Id: Ie085fbcf186e9d7505e1127e0786a12968ebc344 --- core/java/android/view/View.java | 42 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 1c0e73d3321f..f8aef88bc026 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -52,6 +52,7 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.util.FloatProperty; import android.util.Log; +import android.util.LongSparseLongArray; import android.util.Pools.SynchronizedPool; import android.util.Property; import android.util.SparseArray; @@ -2199,6 +2200,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ static final int PFLAG3_HAS_LAYOUT = 0x4; + /** + * Flag indicating that a call to measure() was skipped and should be done + * instead when layout() is invoked. + */ + static final int PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT = 0x8; + /* End of masks for mPrivateFlags3 */ @@ -2979,6 +2986,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ int mOldHeightMeasureSpec = Integer.MIN_VALUE; + private LongSparseLongArray mMeasureCache; + @ViewDebug.ExportedProperty(deepExport = true, prefix = "bg_") private Drawable mBackground; @@ -14401,12 +14410,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @SuppressWarnings({"unchecked"}) public void layout(int l, int t, int r, int b) { + if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) { + onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec); + mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; + } + int oldL = mLeft; int oldT = mTop; int oldB = mBottom; int oldR = mRight; + boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); + if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { onLayout(changed, l, t, r, b); mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED; @@ -14421,6 +14437,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } } + mPrivateFlags &= ~PFLAG_FORCE_LAYOUT; mPrivateFlags3 |= PFLAG3_HAS_LAYOUT; } @@ -15898,6 +15915,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * handle possible request-during-layout errors correctly.

*/ public void requestLayout() { + if (mMeasureCache != null) mMeasureCache.clear(); + if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) { // Only trigger request-during-layout logic if this is the view requesting it, // not the views in its parent hierarchy @@ -15927,6 +15946,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * on the parent. */ public void forceLayout() { + if (mMeasureCache != null) mMeasureCache.clear(); + mPrivateFlags |= PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED; } @@ -15960,6 +15981,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, widthMeasureSpec = MeasureSpec.adjust(widthMeasureSpec, optical ? -oWidth : oWidth); heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight); } + + // Suppress sign extension for the low bytes + long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL; + if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2); + if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT || widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec) { @@ -15969,8 +15995,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, resolveRtlPropertiesIfNeeded(); - // measure ourselves, this should set the measured dimension flag back - onMeasure(widthMeasureSpec, heightMeasureSpec); + int cacheIndex = mMeasureCache.indexOfKey(key); + if (cacheIndex < 0) { + // measure ourselves, this should set the measured dimension flag back + onMeasure(widthMeasureSpec, heightMeasureSpec); + mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; + } else { + long value = mMeasureCache.valueAt(cacheIndex); + // Casting a long to int drops the high 32 bits, no mask needed + setMeasuredDimension((int) (value >> 32), (int) value); + mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; + } // flag not set, setMeasuredDimension() was not invoked, we raise // an exception to warn the developer @@ -15985,6 +16020,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mOldWidthMeasureSpec = widthMeasureSpec; mOldHeightMeasureSpec = heightMeasureSpec; + + mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 | + (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension } /** -- cgit v1.2.3-59-g8ed1b