Home | History | Annotate | Download | only in res
      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      */
     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