ConfigurationBoundResourceCache.java revision d422dc358f0100106dc07d7b903201eb9b043b11
1/*
2* Copyright (C) 2014 The Android Open Source Project
3*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7*
8*      http://www.apache.org/licenses/LICENSE-2.0
9*
10* Unless required by applicable law or agreed to in writing, software
11* distributed under the License is distributed on an "AS IS" BASIS,
12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13* See the License for the specific language governing permissions and
14* limitations under the License.
15*/
16package android.content.res;
17
18import android.util.ArrayMap;
19import android.util.LongSparseArray;
20import java.lang.ref.WeakReference;
21
22/**
23 * A Cache class which can be used to cache resource objects that are easy to clone but more
24 * expensive to inflate.
25 * @hide
26 */
27public class ConfigurationBoundResourceCache<T> {
28
29    private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>> mCache =
30            new ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>>();
31
32    final Resources mResources;
33
34    /**
35     * Creates a Resource cache for the given Resources instance.
36     *
37     * @param resources The Resource which can be used when creating new instances.
38     */
39    public ConfigurationBoundResourceCache(Resources resources) {
40        mResources = resources;
41    }
42
43    /**
44     * Adds a new item to the cache.
45     *
46     * @param key A custom key that uniquely identifies the resource.
47     * @param theme The Theme instance where this resource was loaded.
48     * @param constantState The constant state that can create new instances of the resource.
49     *
50     */
51    public void put(long key, Resources.Theme theme, ConstantState<T> constantState) {
52        if (constantState == null) {
53            return;
54        }
55        final String themeKey = theme == null ? "" : theme.getKey();
56        LongSparseArray<WeakReference<ConstantState<T>>> themedCache;
57        synchronized (this) {
58            themedCache = mCache.get(themeKey);
59            if (themedCache == null) {
60                themedCache = new LongSparseArray<WeakReference<ConstantState<T>>>(1);
61                mCache.put(themeKey, themedCache);
62            }
63            themedCache.put(key, new WeakReference<ConstantState<T>>(constantState));
64        }
65    }
66
67    /**
68     * If the resource is cached, creates a new instance of it and returns.
69     *
70     * @param key The long key which can be used to uniquely identify the resource.
71     * @param theme The The Theme instance where we want to load this resource.
72     *
73     * @return If this resources was loaded before, returns a new instance of it. Otherwise, returns
74     *         null.
75     */
76    public T get(long key, Resources.Theme theme) {
77        final String themeKey = theme != null ? theme.getKey() : "";
78        final LongSparseArray<WeakReference<ConstantState<T>>> themedCache;
79        final WeakReference<ConstantState<T>> wr;
80        synchronized (this) {
81            themedCache = mCache.get(themeKey);
82            if (themedCache == null) {
83                return null;
84            }
85            wr = themedCache.get(key);
86        }
87        if (wr == null) {
88            return null;
89        }
90        final ConstantState entry = wr.get();
91        if (entry != null) {
92            return  (T) entry.newInstance(mResources, theme);
93        } else {  // our entry has been purged
94            synchronized (this) {
95                // there is a potential race condition here where this entry may be put in
96                // another thread. But we prefer it to minimize lock duration
97                themedCache.delete(key);
98            }
99        }
100        return null;
101    }
102
103    /**
104     * Users of ConfigurationBoundResourceCache must call this method whenever a configuration
105     * change happens. On this callback, the cache invalidates all resources that are not valid
106     * anymore.
107     *
108     * @param configChanges The configuration changes
109     */
110    public void onConfigurationChange(final int configChanges) {
111        synchronized (this) {
112            final int size = mCache.size();
113            for (int i = size - 1; i >= 0; i--) {
114                final LongSparseArray<WeakReference<ConstantState<T>>>
115                        themeCache = mCache.valueAt(i);
116                onConfigurationChangeInt(themeCache, configChanges);
117                if (themeCache.size() == 0) {
118                    mCache.removeAt(i);
119                }
120            }
121        }
122    }
123
124    private void onConfigurationChangeInt(
125            final LongSparseArray<WeakReference<ConstantState<T>>> themeCache,
126            final int configChanges) {
127        final int size = themeCache.size();
128        for (int i = size - 1; i >= 0; i--) {
129            final WeakReference<ConstantState<T>> wr = themeCache.valueAt(i);
130            final ConstantState<T> constantState = wr.get();
131            if (constantState == null || Configuration.needNewResources(
132                    configChanges, constantState.getChangingConfigurations())) {
133                themeCache.removeAt(i);
134            }
135        }
136    }
137
138}
139