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