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