blob: cde7e84b70ce7ac33bf8ce7244a632e1a277bf92 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.content.res;
import android.util.ArrayMap;
import android.util.LongSparseArray;
import java.lang.ref.WeakReference;
/**
* A Cache class which can be used to cache resource objects that are easy to clone but more
* expensive to inflate.
* @hide
*/
public class ConfigurationBoundResourceCache<T> {
private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>> mCache =
new ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>>();
final Resources mResources;
/**
* Creates a Resource cache for the given Resources instance.
*
* @param resources The Resource which can be used when creating new instances.
*/
public ConfigurationBoundResourceCache(Resources resources) {
mResources = resources;
}
/**
* Adds a new item to the cache.
*
* @param key A custom key that uniquely identifies the resource.
* @param theme The Theme instance where this resource was loaded.
* @param constantState The constant state that can create new instances of the resource.
*
*/
public void put(long key, Resources.Theme theme, ConstantState<T> constantState) {
if (constantState == null) {
return;
}
final String themeKey = theme == null ? "" : theme.getKey();
LongSparseArray<WeakReference<ConstantState<T>>> themedCache;
synchronized (this) {
themedCache = mCache.get(themeKey);
if (themedCache == null) {
themedCache = new LongSparseArray<WeakReference<ConstantState<T>>>(1);
mCache.put(themeKey, themedCache);
}
themedCache.put(key, new WeakReference<ConstantState<T>>(constantState));
}
}
/**
* If the resource is cached, creates a new instance of it and returns.
*
* @param key The long key which can be used to uniquely identify the resource.
* @param theme The The Theme instance where we want to load this resource.
*
* @return If this resources was loaded before, returns a new instance of it. Otherwise, returns
* null.
*/
public T get(long key, Resources.Theme theme) {
final String themeKey = theme != null ? theme.getKey() : "";
final LongSparseArray<WeakReference<ConstantState<T>>> themedCache;
final WeakReference<ConstantState<T>> wr;
synchronized (this) {
themedCache = mCache.get(themeKey);
if (themedCache == null) {
return null;
}
wr = themedCache.get(key);
}
if (wr == null) {
return null;
}
final ConstantState entry = wr.get();
if (entry != null) {
return (T) entry.newInstance(mResources, theme);
} else { // our entry has been purged
synchronized (this) {
// there is a potential race condition here where this entry may be put in
// another thread. But we prefer it to minimize lock duration
themedCache.delete(key);
}
}
return null;
}
/**
* Users of ConfigurationBoundResourceCache must call this method whenever a configuration
* change happens. On this callback, the cache invalidates all resources that are not valid
* anymore.
*
* @param configChanges The configuration changes
*/
public void onConfigurationChange(final int configChanges) {
synchronized (this) {
final int size = mCache.size();
for (int i = size - 1; i >= 0; i--) {
final LongSparseArray<WeakReference<ConstantState<T>>>
themeCache = mCache.valueAt(i);
onConfigurationChangeInt(themeCache, configChanges);
if (themeCache.size() == 0) {
mCache.removeAt(i);
}
}
}
}
private void onConfigurationChangeInt(
final LongSparseArray<WeakReference<ConstantState<T>>> themeCache,
final int configChanges) {
final int size = themeCache.size();
for (int i = size - 1; i >= 0; i--) {
final WeakReference<ConstantState<T>> wr = themeCache.valueAt(i);
final ConstantState<T> constantState = wr.get();
if (constantState == null || Configuration.needNewResources(
configChanges, constantState.getChangingConfigurations())) {
themeCache.removeAt(i);
}
}
}
}