Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 094f7289 authored by Jeremy Meyer's avatar Jeremy Meyer
Browse files

Improve performance of generations in the resource cache

Tracking generations in the resouce cache caused a performance
regression, likely from allocating an Entry object for every resource
retrieval. This optimizes that by having the retrieval of the value be
separate from retrieval of the generation. This makes it so that on
cache hits there is no difference from before the generation change and
a cache miss only adds the overhead of a method call to retrieve an int.

Test: ran with forest to determine improvement
Fixes: 285575799, 285489092, 285730765, 285607129, 285489541, 285943600
Change-Id: I5144f08bb38d0e600b7f556df834292e50afb91c
parent 8cc5b8a4
Loading
Loading
Loading
Loading
+12 −12
Original line number Diff line number Diff line
@@ -111,20 +111,20 @@ public class AnimatorInflater {
            float pathErrorScale) throws NotFoundException {
        final ConfigurationBoundResourceCache<Animator> animatorCache = resources
                .getAnimatorCache();
        ConfigurationBoundResourceCache.Entry<Animator> animatorEntry =
                animatorCache.getInstance(id, resources, theme);
        if (animatorEntry.hasValue()) {
        Animator animator = animatorCache.getInstance(id, resources, theme);
        if (animator != null) {
            if (DBG_ANIMATOR_INFLATER) {
                Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id));
            }
            return animatorEntry.getValue();
            return animator;
        } else if (DBG_ANIMATOR_INFLATER) {
            Log.d(TAG, "cache miss for animator " + resources.getResourceName(id));
        }
        int cacheGeneration = animatorCache.getGeneration();
        XmlResourceParser parser = null;
        try {
            parser = resources.getAnimation(id);
            Animator animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale);
            animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale);
            if (animator != null) {
                animator.appendChangingConfigurations(getChangingConfigs(resources, id));
                final ConstantState<Animator> constantState = animator.createConstantState();
@@ -132,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, animatorEntry.getGeneration());
                    animatorCache.put(id, theme, constantState, cacheGeneration);
                    // create a new animator so that cached version is never used by the user
                    animator = constantState.newInstance(resources, theme);
                }
@@ -161,22 +161,22 @@ public class AnimatorInflater {
        final ConfigurationBoundResourceCache<StateListAnimator> cache = resources
                .getStateListAnimatorCache();
        final Theme theme = context.getTheme();
        ConfigurationBoundResourceCache.Entry<StateListAnimator> animatorEntry =
                cache.getInstance(id, resources, theme);
        if (animatorEntry.hasValue()) {
            return animatorEntry.getValue();
        StateListAnimator animator = cache.getInstance(id, resources, theme);
        if (animator != null) {
            return animator;
        }
        int cacheGeneration = cache.getGeneration();
        XmlResourceParser parser = null;
        try {
            parser = resources.getAnimation(id);
            StateListAnimator animator =
            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, animatorEntry.getGeneration());
                    cache.put(id, theme, constantState, cacheGeneration);
                    // return a clone so that the animator in constant state is never used.
                    animator = constantState.newInstance(resources, theme);
                }
+6 −6
Original line number Diff line number Diff line
@@ -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 an Entry wrapping a new instance of the resource, or {@code null} if not in
     * @return a new instance of the resource, or {@code null} if not in
     *         the cache
     */
    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());
    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);
        }

        return new Entry<>(null, e.getGeneration());
        return null;
    }

    @Override
+3 −21
Original line number Diff line number Diff line
@@ -40,32 +40,14 @@ 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 Entry<Drawable.ConstantState> entry = get(key, theme);
        if (entry.getValue() != null) {
            return entry.getValue().newDrawable(resources, theme);
        final Drawable.ConstantState entry = get(key, theme);
        if (entry != null) {
            return entry.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());
+10 −17
Original line number Diff line number Diff line
@@ -650,21 +650,16 @@ public class ResourcesImpl {
                key = (((long) value.assetCookie) << 32) | value.data;
            }

            int cacheGeneration;
            int cacheGeneration = caches.getGeneration();
            // 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 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;
                Drawable cachedDrawable = caches.getInstance(key, wrapper, theme);
                if (cachedDrawable != null) {
                    cachedDrawable.setChangingConfigurations(value.changingConfigurations);
                    return cachedDrawable;
                }
            }

            // Next, check preloaded drawables. Preloaded drawables may contain
@@ -1009,16 +1004,15 @@ public class ResourcesImpl {
            TypedValue value, int id) {
        final long key = (((long) value.assetCookie) << 32) | value.data;
        final ConfigurationBoundResourceCache<ComplexColor> cache = mComplexColorCache;
        ThemedResourceCache.Entry<ComplexColor> complexColorEntry =
                cache.getInstance(key, wrapper, theme);
        if (complexColorEntry.hasValue()) {
            return complexColorEntry.getValue();
        ComplexColor complexColor = cache.getInstance(key, wrapper, theme);
        if (complexColor != null) {
            return complexColor;
        }
        int cacheGeneration = cache.getGeneration();

        final android.content.res.ConstantState<ComplexColor> factory =
                sPreloadedComplexColors.get(key);

        ComplexColor complexColor = null;
        if (factory != null) {
            complexColor = factory.newInstance(wrapper, theme);
        }
@@ -1035,8 +1029,7 @@ public class ResourcesImpl {
                    sPreloadedComplexColors.put(key, complexColor.getConstantState());
                }
            } else {
                cache.put(key, theme, complexColor.getConstantState(),
                        complexColorEntry.getGeneration());
                cache.put(key, theme, complexColor.getConstantState(), cacheGeneration);
            }
        }
        return complexColor;
+14 −27
Original line number Diff line number Diff line
@@ -41,29 +41,6 @@ abstract class ThemedResourceCache<T> {

    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.
     *
@@ -108,6 +85,15 @@ abstract class ThemedResourceCache<T> {
        }
    }

    /**
     * Returns the current generation of the cache
     *
     * @return The current generation
     */
    public int getGeneration() {
        return mGeneration;
    }

    /**
     * Returns an entry from the cache.
     *
@@ -116,7 +102,7 @@ abstract class ThemedResourceCache<T> {
     * @return a cached entry, or {@code null} if not in the cache
     */
    @Nullable
    public Entry get(long key, @Nullable Theme theme) {
    public T 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
@@ -126,7 +112,7 @@ abstract class ThemedResourceCache<T> {
            if (themedEntries != null) {
                final WeakReference<T> themedEntry = themedEntries.get(key);
                if (themedEntry != null) {
                    return new Entry(themedEntry.get(), mGeneration);
                    return themedEntry.get();
                }
            }

@@ -134,14 +120,15 @@ abstract class ThemedResourceCache<T> {
            if (unthemedEntries != null) {
                final WeakReference<T> unthemedEntry = unthemedEntries.get(key);
                if (unthemedEntry != null) {
                    return new Entry(unthemedEntry.get(), mGeneration);
                    return unthemedEntry.get();
                }
            }
        }

        return new Entry(null, mGeneration);
        return null;
    }


    /**
     * Prunes cache entries that have been invalidated by a configuration
     * change.
Loading