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 */
16 package android.content.res;
17 
18 import android.util.ArrayMap;
19 import android.util.LongSparseArray;
20 import 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  */
27 public 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      */
ConfigurationBoundResourceCache(Resources resources)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      */
put(long key, Resources.Theme theme, ConstantState<T> constantState)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      */
get(long key, Resources.Theme theme)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      */
onConfigurationChange(final int configChanges)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 
onConfigurationChangeInt( final LongSparseArray<WeakReference<ConstantState<T>>> themeCache, final int configChanges)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