Loading core/java/android/animation/AnimatorInflater.java +13 −10 Original line number Diff line number Diff line Loading @@ -111,19 +111,20 @@ public class AnimatorInflater { float pathErrorScale) throws NotFoundException { final ConfigurationBoundResourceCache<Animator> animatorCache = resources .getAnimatorCache(); Animator animator = animatorCache.getInstance(id, resources, theme); if (animator != null) { ConfigurationBoundResourceCache.Entry<Animator> animatorEntry = animatorCache.getInstance(id, resources, theme); if (animatorEntry.hasValue()) { if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id)); } return animator; return animatorEntry.getValue(); } else if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "cache miss for animator " + resources.getResourceName(id)); } XmlResourceParser parser = null; try { parser = resources.getAnimation(id); animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale); Animator animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale); if (animator != null) { animator.appendChangingConfigurations(getChangingConfigs(resources, id)); final ConstantState<Animator> constantState = animator.createConstantState(); Loading @@ -131,7 +132,7 @@ public class AnimatorInflater { if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "caching animator for res " + resources.getResourceName(id)); } animatorCache.put(id, theme, constantState); animatorCache.put(id, theme, constantState, animatorEntry.getGeneration()); // create a new animator so that cached version is never used by the user animator = constantState.newInstance(resources, theme); } Loading Loading @@ -160,20 +161,22 @@ public class AnimatorInflater { final ConfigurationBoundResourceCache<StateListAnimator> cache = resources .getStateListAnimatorCache(); final Theme theme = context.getTheme(); StateListAnimator animator = cache.getInstance(id, resources, theme); if (animator != null) { return animator; ConfigurationBoundResourceCache.Entry<StateListAnimator> animatorEntry = cache.getInstance(id, resources, theme); if (animatorEntry.hasValue()) { return animatorEntry.getValue(); } XmlResourceParser parser = null; try { parser = resources.getAnimation(id); animator = createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser)); StateListAnimator animator = createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser)); if (animator != null) { animator.appendChangingConfigurations(getChangingConfigs(resources, id)); final ConstantState<StateListAnimator> constantState = animator .createConstantState(); if (constantState != null) { cache.put(id, theme, constantState); cache.put(id, theme, constantState, animatorEntry.getGeneration()); // return a clone so that the animator in constant state is never used. animator = constantState.newInstance(resources, theme); } Loading core/java/android/content/res/ConfigurationBoundResourceCache.java +6 −6 Original line number Diff line number Diff line Loading @@ -37,16 +37,16 @@ public class ConfigurationBoundResourceCache<T> extends ThemedResourceCache<Cons * @param key a key that uniquely identifies the drawable resource * @param resources a Resources object from which to create new instances. * @param theme the theme where the resource will be used * @return a new instance of the resource, or {@code null} if not in * @return an Entry wrapping a new instance of the resource, or {@code null} if not in * the cache */ public T getInstance(long key, Resources resources, Resources.Theme theme) { final ConstantState<T> entry = get(key, theme); if (entry != null) { return entry.newInstance(resources, theme); public Entry<T> getInstance(long key, Resources resources, Resources.Theme theme) { final Entry<ConstantState<T>> e = get(key, theme); if (e.hasValue()) { return new Entry<>(e.getValue().newInstance(resources, theme), e.getGeneration()); } return null; return new Entry<>(null, e.getGeneration()); } @Override Loading core/java/android/content/res/DrawableCache.java +21 −3 Original line number Diff line number Diff line Loading @@ -40,14 +40,32 @@ class DrawableCache extends ThemedResourceCache<Drawable.ConstantState> { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public Drawable getInstance(long key, Resources resources, Resources.Theme theme) { final Drawable.ConstantState entry = get(key, theme); if (entry != null) { return entry.newDrawable(resources, theme); final Entry<Drawable.ConstantState> entry = get(key, theme); if (entry.getValue() != null) { return entry.getValue().newDrawable(resources, theme); } return null; } /** * If the resource is cached, creates and returns a new instance of it. * * @param key a key that uniquely identifies the drawable resource * @param resources a Resources object from which to create new instances. * @param theme the theme where the resource will be used * @return an Entry wrapping a a new instance of the resource, or {@code null} if not in * the cache */ public Entry<Drawable> getDrawable(long key, Resources resources, Resources.Theme theme) { final Entry<Drawable.ConstantState> e = get(key, theme); if (e.hasValue()) { return new Entry<>(e.getValue().newDrawable(resources, theme), e.getGeneration()); } return new Entry<>(null, e.getGeneration()); } @Override public boolean shouldInvalidateEntry(Drawable.ConstantState entry, int configChanges) { return Configuration.needNewResources(configChanges, entry.getChangingConfigurations()); Loading core/java/android/content/res/ResourcesImpl.java +21 −11 Original line number Diff line number Diff line Loading @@ -650,15 +650,21 @@ public class ResourcesImpl { key = (((long) value.assetCookie) << 32) | value.data; } int cacheGeneration; // First, check whether we have a cached version of this drawable // that was inflated against the specified theme. Skip the cache if // we're currently preloading or we're not using the cache. if (!mPreloading && useCache) { final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme); if (cachedDrawable != null) { cachedDrawable.setChangingConfigurations(value.changingConfigurations); return cachedDrawable; } final ThemedResourceCache.Entry<Drawable> cachedDrawable = caches.getDrawable(key, wrapper, theme); if (cachedDrawable.hasValue()) { cachedDrawable.getValue().setChangingConfigurations( value.changingConfigurations); return cachedDrawable.getValue(); } cacheGeneration = cachedDrawable.getGeneration(); } else { cacheGeneration = ThemedResourceCache.UNDEFINED_GENERATION; } // Next, check preloaded drawables. Preloaded drawables may contain Loading Loading @@ -702,7 +708,8 @@ public class ResourcesImpl { if (dr != null) { dr.setChangingConfigurations(value.changingConfigurations); if (useCache) { cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr); cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr, cacheGeneration); if (needsNewDrawableAfterCache) { Drawable.ConstantState state = dr.getConstantState(); if (state != null) { Loading Loading @@ -733,7 +740,7 @@ public class ResourcesImpl { } private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches, Resources.Theme theme, boolean usesTheme, long key, Drawable dr) { Resources.Theme theme, boolean usesTheme, long key, Drawable dr, int cacheGeneration) { final Drawable.ConstantState cs = dr.getConstantState(); if (cs == null) { return; Loading Loading @@ -761,7 +768,7 @@ public class ResourcesImpl { } } else { synchronized (mAccessLock) { caches.put(key, theme, cs, usesTheme); caches.put(key, theme, cs, cacheGeneration, usesTheme); } } } Loading Loading @@ -1002,14 +1009,16 @@ public class ResourcesImpl { TypedValue value, int id) { final long key = (((long) value.assetCookie) << 32) | value.data; final ConfigurationBoundResourceCache<ComplexColor> cache = mComplexColorCache; ComplexColor complexColor = cache.getInstance(key, wrapper, theme); if (complexColor != null) { return complexColor; ThemedResourceCache.Entry<ComplexColor> complexColorEntry = cache.getInstance(key, wrapper, theme); if (complexColorEntry.hasValue()) { return complexColorEntry.getValue(); } final android.content.res.ConstantState<ComplexColor> factory = sPreloadedComplexColors.get(key); ComplexColor complexColor = null; if (factory != null) { complexColor = factory.newInstance(wrapper, theme); } Loading @@ -1026,7 +1035,8 @@ public class ResourcesImpl { sPreloadedComplexColors.put(key, complexColor.getConstantState()); } } else { cache.put(key, theme, complexColor.getConstantState()); cache.put(key, theme, complexColor.getConstantState(), complexColorEntry.getGeneration()); } } return complexColor; Loading core/java/android/content/res/ThemedResourceCache.java +39 −8 Original line number Diff line number Diff line Loading @@ -33,11 +33,37 @@ import java.lang.ref.WeakReference; * @param <T> type of data to cache */ abstract class ThemedResourceCache<T> { public static final int UNDEFINED_GENERATION = -1; @UnsupportedAppUsage private ArrayMap<ThemeKey, LongSparseArray<WeakReference<T>>> mThemedEntries; private LongSparseArray<WeakReference<T>> mUnthemedEntries; private LongSparseArray<WeakReference<T>> mNullThemedEntries; private int mGeneration; public static class Entry<S> { private S mValue; private int mGeneration; public S getValue() { return mValue; } public boolean hasValue() { return mValue != null; } public int getGeneration() { return mGeneration; } Entry(S value, int generation) { this.mValue = value; this.mGeneration = generation; } } /** * Adds a new theme-dependent entry to the cache. * Loading @@ -45,9 +71,10 @@ abstract class ThemedResourceCache<T> { * @param theme the theme against which this entry was inflated, or * {@code null} if the entry has no theme applied * @param entry the entry to cache * @param generation The generation of the cache to compare against before storing */ public void put(long key, @Nullable Theme theme, @NonNull T entry) { put(key, theme, entry, true); public void put(long key, @Nullable Theme theme, @NonNull T entry, int generation) { put(key, theme, entry, generation, true); } /** Loading @@ -57,10 +84,12 @@ abstract class ThemedResourceCache<T> { * @param theme the theme against which this entry was inflated, or * {@code null} if the entry has no theme applied * @param entry the entry to cache * @param generation The generation of the cache to compare against before storing * @param usesTheme {@code true} if the entry is affected theme changes, * {@code false} otherwise */ public void put(long key, @Nullable Theme theme, @NonNull T entry, boolean usesTheme) { public void put(long key, @Nullable Theme theme, @NonNull T entry, int generation, boolean usesTheme) { if (entry == null) { return; } Loading @@ -72,7 +101,8 @@ abstract class ThemedResourceCache<T> { } else { entries = getThemedLocked(theme, true); } if (entries != null) { if (entries != null && ((generation == mGeneration) || (generation == UNDEFINED_GENERATION))) { entries.put(key, new WeakReference<>(entry)); } } Loading @@ -86,7 +116,7 @@ abstract class ThemedResourceCache<T> { * @return a cached entry, or {@code null} if not in the cache */ @Nullable public T get(long key, @Nullable Theme theme) { public Entry get(long key, @Nullable Theme theme) { // The themed (includes null-themed) and unthemed caches are mutually // exclusive, so we'll give priority to whichever one we think we'll // hit first. Since most of the framework drawables are themed, that's Loading @@ -96,7 +126,7 @@ abstract class ThemedResourceCache<T> { if (themedEntries != null) { final WeakReference<T> themedEntry = themedEntries.get(key); if (themedEntry != null) { return themedEntry.get(); return new Entry(themedEntry.get(), mGeneration); } } Loading @@ -104,12 +134,12 @@ abstract class ThemedResourceCache<T> { if (unthemedEntries != null) { final WeakReference<T> unthemedEntry = unthemedEntries.get(key); if (unthemedEntry != null) { return unthemedEntry.get(); return new Entry(unthemedEntry.get(), mGeneration); } } } return null; return new Entry(null, mGeneration); } /** Loading @@ -121,6 +151,7 @@ abstract class ThemedResourceCache<T> { @UnsupportedAppUsage public void onConfigurationChange(@Config int configChanges) { prune(configChanges); mGeneration++; } /** Loading Loading
core/java/android/animation/AnimatorInflater.java +13 −10 Original line number Diff line number Diff line Loading @@ -111,19 +111,20 @@ public class AnimatorInflater { float pathErrorScale) throws NotFoundException { final ConfigurationBoundResourceCache<Animator> animatorCache = resources .getAnimatorCache(); Animator animator = animatorCache.getInstance(id, resources, theme); if (animator != null) { ConfigurationBoundResourceCache.Entry<Animator> animatorEntry = animatorCache.getInstance(id, resources, theme); if (animatorEntry.hasValue()) { if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id)); } return animator; return animatorEntry.getValue(); } else if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "cache miss for animator " + resources.getResourceName(id)); } XmlResourceParser parser = null; try { parser = resources.getAnimation(id); animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale); Animator animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale); if (animator != null) { animator.appendChangingConfigurations(getChangingConfigs(resources, id)); final ConstantState<Animator> constantState = animator.createConstantState(); Loading @@ -131,7 +132,7 @@ public class AnimatorInflater { if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "caching animator for res " + resources.getResourceName(id)); } animatorCache.put(id, theme, constantState); animatorCache.put(id, theme, constantState, animatorEntry.getGeneration()); // create a new animator so that cached version is never used by the user animator = constantState.newInstance(resources, theme); } Loading Loading @@ -160,20 +161,22 @@ public class AnimatorInflater { final ConfigurationBoundResourceCache<StateListAnimator> cache = resources .getStateListAnimatorCache(); final Theme theme = context.getTheme(); StateListAnimator animator = cache.getInstance(id, resources, theme); if (animator != null) { return animator; ConfigurationBoundResourceCache.Entry<StateListAnimator> animatorEntry = cache.getInstance(id, resources, theme); if (animatorEntry.hasValue()) { return animatorEntry.getValue(); } XmlResourceParser parser = null; try { parser = resources.getAnimation(id); animator = createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser)); StateListAnimator animator = createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser)); if (animator != null) { animator.appendChangingConfigurations(getChangingConfigs(resources, id)); final ConstantState<StateListAnimator> constantState = animator .createConstantState(); if (constantState != null) { cache.put(id, theme, constantState); cache.put(id, theme, constantState, animatorEntry.getGeneration()); // return a clone so that the animator in constant state is never used. animator = constantState.newInstance(resources, theme); } Loading
core/java/android/content/res/ConfigurationBoundResourceCache.java +6 −6 Original line number Diff line number Diff line Loading @@ -37,16 +37,16 @@ public class ConfigurationBoundResourceCache<T> extends ThemedResourceCache<Cons * @param key a key that uniquely identifies the drawable resource * @param resources a Resources object from which to create new instances. * @param theme the theme where the resource will be used * @return a new instance of the resource, or {@code null} if not in * @return an Entry wrapping a new instance of the resource, or {@code null} if not in * the cache */ public T getInstance(long key, Resources resources, Resources.Theme theme) { final ConstantState<T> entry = get(key, theme); if (entry != null) { return entry.newInstance(resources, theme); public Entry<T> getInstance(long key, Resources resources, Resources.Theme theme) { final Entry<ConstantState<T>> e = get(key, theme); if (e.hasValue()) { return new Entry<>(e.getValue().newInstance(resources, theme), e.getGeneration()); } return null; return new Entry<>(null, e.getGeneration()); } @Override Loading
core/java/android/content/res/DrawableCache.java +21 −3 Original line number Diff line number Diff line Loading @@ -40,14 +40,32 @@ class DrawableCache extends ThemedResourceCache<Drawable.ConstantState> { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public Drawable getInstance(long key, Resources resources, Resources.Theme theme) { final Drawable.ConstantState entry = get(key, theme); if (entry != null) { return entry.newDrawable(resources, theme); final Entry<Drawable.ConstantState> entry = get(key, theme); if (entry.getValue() != null) { return entry.getValue().newDrawable(resources, theme); } return null; } /** * If the resource is cached, creates and returns a new instance of it. * * @param key a key that uniquely identifies the drawable resource * @param resources a Resources object from which to create new instances. * @param theme the theme where the resource will be used * @return an Entry wrapping a a new instance of the resource, or {@code null} if not in * the cache */ public Entry<Drawable> getDrawable(long key, Resources resources, Resources.Theme theme) { final Entry<Drawable.ConstantState> e = get(key, theme); if (e.hasValue()) { return new Entry<>(e.getValue().newDrawable(resources, theme), e.getGeneration()); } return new Entry<>(null, e.getGeneration()); } @Override public boolean shouldInvalidateEntry(Drawable.ConstantState entry, int configChanges) { return Configuration.needNewResources(configChanges, entry.getChangingConfigurations()); Loading
core/java/android/content/res/ResourcesImpl.java +21 −11 Original line number Diff line number Diff line Loading @@ -650,15 +650,21 @@ public class ResourcesImpl { key = (((long) value.assetCookie) << 32) | value.data; } int cacheGeneration; // First, check whether we have a cached version of this drawable // that was inflated against the specified theme. Skip the cache if // we're currently preloading or we're not using the cache. if (!mPreloading && useCache) { final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme); if (cachedDrawable != null) { cachedDrawable.setChangingConfigurations(value.changingConfigurations); return cachedDrawable; } final ThemedResourceCache.Entry<Drawable> cachedDrawable = caches.getDrawable(key, wrapper, theme); if (cachedDrawable.hasValue()) { cachedDrawable.getValue().setChangingConfigurations( value.changingConfigurations); return cachedDrawable.getValue(); } cacheGeneration = cachedDrawable.getGeneration(); } else { cacheGeneration = ThemedResourceCache.UNDEFINED_GENERATION; } // Next, check preloaded drawables. Preloaded drawables may contain Loading Loading @@ -702,7 +708,8 @@ public class ResourcesImpl { if (dr != null) { dr.setChangingConfigurations(value.changingConfigurations); if (useCache) { cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr); cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr, cacheGeneration); if (needsNewDrawableAfterCache) { Drawable.ConstantState state = dr.getConstantState(); if (state != null) { Loading Loading @@ -733,7 +740,7 @@ public class ResourcesImpl { } private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches, Resources.Theme theme, boolean usesTheme, long key, Drawable dr) { Resources.Theme theme, boolean usesTheme, long key, Drawable dr, int cacheGeneration) { final Drawable.ConstantState cs = dr.getConstantState(); if (cs == null) { return; Loading Loading @@ -761,7 +768,7 @@ public class ResourcesImpl { } } else { synchronized (mAccessLock) { caches.put(key, theme, cs, usesTheme); caches.put(key, theme, cs, cacheGeneration, usesTheme); } } } Loading Loading @@ -1002,14 +1009,16 @@ public class ResourcesImpl { TypedValue value, int id) { final long key = (((long) value.assetCookie) << 32) | value.data; final ConfigurationBoundResourceCache<ComplexColor> cache = mComplexColorCache; ComplexColor complexColor = cache.getInstance(key, wrapper, theme); if (complexColor != null) { return complexColor; ThemedResourceCache.Entry<ComplexColor> complexColorEntry = cache.getInstance(key, wrapper, theme); if (complexColorEntry.hasValue()) { return complexColorEntry.getValue(); } final android.content.res.ConstantState<ComplexColor> factory = sPreloadedComplexColors.get(key); ComplexColor complexColor = null; if (factory != null) { complexColor = factory.newInstance(wrapper, theme); } Loading @@ -1026,7 +1035,8 @@ public class ResourcesImpl { sPreloadedComplexColors.put(key, complexColor.getConstantState()); } } else { cache.put(key, theme, complexColor.getConstantState()); cache.put(key, theme, complexColor.getConstantState(), complexColorEntry.getGeneration()); } } return complexColor; Loading
core/java/android/content/res/ThemedResourceCache.java +39 −8 Original line number Diff line number Diff line Loading @@ -33,11 +33,37 @@ import java.lang.ref.WeakReference; * @param <T> type of data to cache */ abstract class ThemedResourceCache<T> { public static final int UNDEFINED_GENERATION = -1; @UnsupportedAppUsage private ArrayMap<ThemeKey, LongSparseArray<WeakReference<T>>> mThemedEntries; private LongSparseArray<WeakReference<T>> mUnthemedEntries; private LongSparseArray<WeakReference<T>> mNullThemedEntries; private int mGeneration; public static class Entry<S> { private S mValue; private int mGeneration; public S getValue() { return mValue; } public boolean hasValue() { return mValue != null; } public int getGeneration() { return mGeneration; } Entry(S value, int generation) { this.mValue = value; this.mGeneration = generation; } } /** * Adds a new theme-dependent entry to the cache. * Loading @@ -45,9 +71,10 @@ abstract class ThemedResourceCache<T> { * @param theme the theme against which this entry was inflated, or * {@code null} if the entry has no theme applied * @param entry the entry to cache * @param generation The generation of the cache to compare against before storing */ public void put(long key, @Nullable Theme theme, @NonNull T entry) { put(key, theme, entry, true); public void put(long key, @Nullable Theme theme, @NonNull T entry, int generation) { put(key, theme, entry, generation, true); } /** Loading @@ -57,10 +84,12 @@ abstract class ThemedResourceCache<T> { * @param theme the theme against which this entry was inflated, or * {@code null} if the entry has no theme applied * @param entry the entry to cache * @param generation The generation of the cache to compare against before storing * @param usesTheme {@code true} if the entry is affected theme changes, * {@code false} otherwise */ public void put(long key, @Nullable Theme theme, @NonNull T entry, boolean usesTheme) { public void put(long key, @Nullable Theme theme, @NonNull T entry, int generation, boolean usesTheme) { if (entry == null) { return; } Loading @@ -72,7 +101,8 @@ abstract class ThemedResourceCache<T> { } else { entries = getThemedLocked(theme, true); } if (entries != null) { if (entries != null && ((generation == mGeneration) || (generation == UNDEFINED_GENERATION))) { entries.put(key, new WeakReference<>(entry)); } } Loading @@ -86,7 +116,7 @@ abstract class ThemedResourceCache<T> { * @return a cached entry, or {@code null} if not in the cache */ @Nullable public T get(long key, @Nullable Theme theme) { public Entry get(long key, @Nullable Theme theme) { // The themed (includes null-themed) and unthemed caches are mutually // exclusive, so we'll give priority to whichever one we think we'll // hit first. Since most of the framework drawables are themed, that's Loading @@ -96,7 +126,7 @@ abstract class ThemedResourceCache<T> { if (themedEntries != null) { final WeakReference<T> themedEntry = themedEntries.get(key); if (themedEntry != null) { return themedEntry.get(); return new Entry(themedEntry.get(), mGeneration); } } Loading @@ -104,12 +134,12 @@ abstract class ThemedResourceCache<T> { if (unthemedEntries != null) { final WeakReference<T> unthemedEntry = unthemedEntries.get(key); if (unthemedEntry != null) { return unthemedEntry.get(); return new Entry(unthemedEntry.get(), mGeneration); } } } return null; return new Entry(null, mGeneration); } /** Loading @@ -121,6 +151,7 @@ abstract class ThemedResourceCache<T> { @UnsupportedAppUsage public void onConfigurationChange(@Config int configChanges) { prune(configChanges); mGeneration++; } /** Loading