am 11a8748c: am 6a8c79bb: Merge "Improve keying for theme caches, rebase system theme on config change" into mnc-dev
* commit '11a8748c43985159ec910832f06ab6299df26787': Improve keying for theme caches, rebase system theme on config change
This commit is contained in:
@@ -108,7 +108,7 @@ public class AnimatorInflater {
|
||||
float pathErrorScale) throws NotFoundException {
|
||||
final ConfigurationBoundResourceCache<Animator> animatorCache = resources
|
||||
.getAnimatorCache();
|
||||
Animator animator = animatorCache.get(id, theme);
|
||||
Animator animator = animatorCache.getInstance(id, theme);
|
||||
if (animator != null) {
|
||||
if (DBG_ANIMATOR_INFLATER) {
|
||||
Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id));
|
||||
@@ -157,7 +157,7 @@ public class AnimatorInflater {
|
||||
final ConfigurationBoundResourceCache<StateListAnimator> cache = resources
|
||||
.getStateListAnimatorCache();
|
||||
final Theme theme = context.getTheme();
|
||||
StateListAnimator animator = cache.get(id, theme);
|
||||
StateListAnimator animator = cache.getInstance(id, theme);
|
||||
if (animator != null) {
|
||||
return animator;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import android.content.res.AssetManager;
|
||||
import android.content.res.CompatibilityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteDebug;
|
||||
import android.database.sqlite.SQLiteDebug.DbStats;
|
||||
@@ -4186,9 +4187,14 @@ public final class ActivityThread {
|
||||
if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
|
||||
return;
|
||||
}
|
||||
configDiff = mConfiguration.diff(config);
|
||||
mConfiguration.updateFrom(config);
|
||||
|
||||
configDiff = mConfiguration.updateFrom(config);
|
||||
config = applyCompatConfiguration(mCurDefaultDisplayDpi);
|
||||
|
||||
final Theme systemTheme = getSystemContext().getTheme();
|
||||
if ((systemTheme.getChangingConfigurations() & configDiff) != 0) {
|
||||
systemTheme.rebase();
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config);
|
||||
|
||||
@@ -785,6 +785,7 @@ public final class AssetManager implements AutoCloseable {
|
||||
private native final void deleteTheme(long theme);
|
||||
/*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force);
|
||||
/*package*/ native static final void copyTheme(long dest, long source);
|
||||
/*package*/ native static final void clearTheme(long theme);
|
||||
/*package*/ native static final int loadThemeAttributeValue(long theme, int ident,
|
||||
TypedValue outValue,
|
||||
boolean resolve);
|
||||
|
||||
@@ -1,138 +1,58 @@
|
||||
/*
|
||||
* 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;
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import android.util.ArrayMap;
|
||||
import android.util.LongSparseArray;
|
||||
import java.lang.ref.WeakReference;
|
||||
package android.content.res;
|
||||
|
||||
/**
|
||||
* A Cache class which can be used to cache resource objects that are easy to clone but more
|
||||
* expensive to inflate.
|
||||
* @hide
|
||||
*
|
||||
* @hide For internal use only.
|
||||
*/
|
||||
public class ConfigurationBoundResourceCache<T> {
|
||||
|
||||
private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>> mCache =
|
||||
new ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>>();
|
||||
|
||||
final Resources mResources;
|
||||
public class ConfigurationBoundResourceCache<T> extends ThemedResourceCache<ConstantState<T>> {
|
||||
private final Resources mResources;
|
||||
|
||||
/**
|
||||
* Creates a Resource cache for the given Resources instance.
|
||||
* Creates a cache for the given Resources instance.
|
||||
*
|
||||
* @param resources The Resource which can be used when creating new instances.
|
||||
* @param resources the resources to use 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.
|
||||
* If the resource is cached, creates and returns a new instance of it.
|
||||
*
|
||||
* @param key a key that uniquely identifies the drawable resource
|
||||
* @param theme the theme where the resource will be used
|
||||
* @return a new instance of the resource, or {@code null} if not in
|
||||
* the cache
|
||||
*/
|
||||
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();
|
||||
public T getInstance(long key, Resources.Theme theme) {
|
||||
final ConstantState<T> entry = get(key, theme);
|
||||
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 entry.newInstance(mResources, theme);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean shouldInvalidateEntry(ConstantState<T> entry, int configChanges) {
|
||||
return Configuration.needNewResources(configChanges, entry.getChangingConfigurations());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
57
core/java/android/content/res/DrawableCache.java
Normal file
57
core/java/android/content/res/DrawableCache.java
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.graphics.drawable.Drawable;
|
||||
|
||||
/**
|
||||
* Class which can be used to cache Drawable resources against a theme.
|
||||
*/
|
||||
class DrawableCache extends ThemedResourceCache<Drawable.ConstantState> {
|
||||
private final Resources mResources;
|
||||
|
||||
/**
|
||||
* Creates a cache for the given Resources instance.
|
||||
*
|
||||
* @param resources the resources to use when creating new instances
|
||||
*/
|
||||
public DrawableCache(Resources resources) {
|
||||
mResources = resources;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the resource is cached, creates and returns a new instance of it.
|
||||
*
|
||||
* @param key a key that uniquely identifies the drawable resource
|
||||
* @param theme the theme where the resource will be used
|
||||
* @return a new instance of the resource, or {@code null} if not in
|
||||
* the cache
|
||||
*/
|
||||
public Drawable getInstance(long key, Resources.Theme theme) {
|
||||
final Drawable.ConstantState entry = get(key, theme);
|
||||
if (entry != null) {
|
||||
return entry.newDrawable(mResources, theme);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldInvalidateEntry(Drawable.ConstantState entry, int configChanges) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@ import android.annotation.AttrRes;
|
||||
import android.annotation.ColorInt;
|
||||
import android.annotation.StyleRes;
|
||||
import android.annotation.StyleableRes;
|
||||
|
||||
import com.android.internal.util.GrowingArrayUtils;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
@@ -132,10 +134,8 @@ public class Resources {
|
||||
// These are protected by mAccessLock.
|
||||
private final Object mAccessLock = new Object();
|
||||
private final Configuration mTmpConfig = new Configuration();
|
||||
private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mDrawableCache =
|
||||
new ArrayMap<>();
|
||||
private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mColorDrawableCache =
|
||||
new ArrayMap<>();
|
||||
private final DrawableCache mDrawableCache = new DrawableCache(this);
|
||||
private final DrawableCache mColorDrawableCache = new DrawableCache(this);
|
||||
private final ConfigurationBoundResourceCache<ColorStateList> mColorStateListCache =
|
||||
new ConfigurationBoundResourceCache<>(this);
|
||||
private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
|
||||
@@ -1441,7 +1441,7 @@ public class Resources {
|
||||
AssetManager.applyThemeStyle(mTheme, resId, force);
|
||||
|
||||
mThemeResId = resId;
|
||||
mKey += Integer.toHexString(resId) + (force ? "! " : " ");
|
||||
mKey.append(resId, force);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1457,7 +1457,7 @@ public class Resources {
|
||||
AssetManager.copyTheme(mTheme, other.mTheme);
|
||||
|
||||
mThemeResId = other.mThemeResId;
|
||||
mKey = other.mKey;
|
||||
mKey.setTo(other.getKey());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1765,6 +1765,9 @@ public class Resources {
|
||||
mTheme = mAssets.createTheme();
|
||||
}
|
||||
|
||||
/** Unique key for the series of styles applied to this theme. */
|
||||
private final ThemeKey mKey = new ThemeKey();
|
||||
|
||||
@SuppressWarnings("hiding")
|
||||
private final AssetManager mAssets;
|
||||
private final long mTheme;
|
||||
@@ -1772,9 +1775,6 @@ public class Resources {
|
||||
/** Resource identifier for the theme. */
|
||||
private int mThemeResId = 0;
|
||||
|
||||
/** Unique key for the series of styles applied to this theme. */
|
||||
private String mKey = "";
|
||||
|
||||
// Needed by layoutlib.
|
||||
/*package*/ long getNativeTheme() {
|
||||
return mTheme;
|
||||
@@ -1784,7 +1784,7 @@ public class Resources {
|
||||
return mThemeResId;
|
||||
}
|
||||
|
||||
/*package*/ String getKey() {
|
||||
/*package*/ ThemeKey getKey() {
|
||||
return mKey;
|
||||
}
|
||||
|
||||
@@ -1793,29 +1793,119 @@ public class Resources {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses {@link #mKey} and returns a String array that holds pairs of adjacent Theme data:
|
||||
* resource name followed by whether or not it was forced, as specified by
|
||||
* {@link #applyStyle(int, boolean)}.
|
||||
* Parses {@link #mKey} and returns a String array that holds pairs of
|
||||
* adjacent Theme data: resource name followed by whether or not it was
|
||||
* forced, as specified by {@link #applyStyle(int, boolean)}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@ViewDebug.ExportedProperty(category = "theme", hasAdjacentMapping = true)
|
||||
public String[] getTheme() {
|
||||
String[] themeData = mKey.split(" ");
|
||||
String[] themes = new String[themeData.length * 2];
|
||||
String theme;
|
||||
boolean forced;
|
||||
|
||||
for (int i = 0, j = themeData.length - 1; i < themes.length; i += 2, --j) {
|
||||
theme = themeData[j];
|
||||
forced = theme.endsWith("!");
|
||||
themes[i] = forced ?
|
||||
getResourceNameFromHexString(theme.substring(0, theme.length() - 1)) :
|
||||
getResourceNameFromHexString(theme);
|
||||
final int N = mKey.mCount;
|
||||
final String[] themes = new String[N * 2];
|
||||
for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) {
|
||||
final int resId = mKey.mResId[i];
|
||||
final boolean forced = mKey.mForce[i];
|
||||
themes[i] = getResourceName(resId);
|
||||
themes[i + 1] = forced ? "forced" : "not forced";
|
||||
}
|
||||
return themes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebases the theme against the parent Resource object's current
|
||||
* configuration by re-applying the styles passed to
|
||||
* {@link #applyStyle(int, boolean)}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void rebase() {
|
||||
AssetManager.clearTheme(mTheme);
|
||||
|
||||
// Reapply the same styles in the same order.
|
||||
for (int i = 0; i < mKey.mCount; i++) {
|
||||
final int resId = mKey.mResId[i];
|
||||
final boolean force = mKey.mForce[i];
|
||||
AssetManager.applyThemeStyle(mTheme, resId, force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class ThemeKey implements Cloneable {
|
||||
int[] mResId;
|
||||
boolean[] mForce;
|
||||
int mCount;
|
||||
|
||||
private int mHashCode = 0;
|
||||
|
||||
public void append(int resId, boolean force) {
|
||||
if (mResId == null) {
|
||||
mResId = new int[4];
|
||||
}
|
||||
|
||||
if (mForce == null) {
|
||||
mForce = new boolean[4];
|
||||
}
|
||||
|
||||
mResId = GrowingArrayUtils.append(mResId, mCount, resId);
|
||||
mForce = GrowingArrayUtils.append(mForce, mCount, force);
|
||||
mCount++;
|
||||
|
||||
mHashCode = 31 * (31 * mHashCode + resId) + (force ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up this key as a deep copy of another key.
|
||||
*
|
||||
* @param other the key to deep copy into this key
|
||||
*/
|
||||
public void setTo(ThemeKey other) {
|
||||
mResId = other.mResId == null ? null : other.mResId.clone();
|
||||
mForce = other.mForce == null ? null : other.mForce.clone();
|
||||
mCount = other.mCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mHashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (o == null || getClass() != o.getClass() || hashCode() != o.hashCode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ThemeKey t = (ThemeKey) o;
|
||||
if (mCount != t.mCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int N = mCount;
|
||||
for (int i = 0; i < N; i++) {
|
||||
if (mResId[i] != t.mResId[i] || mForce[i] != t.mForce[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a shallow copy of this key
|
||||
*/
|
||||
@Override
|
||||
public ThemeKey clone() {
|
||||
final ThemeKey other = new ThemeKey();
|
||||
other.mResId = mResId;
|
||||
other.mForce = mForce;
|
||||
other.mCount = mCount;
|
||||
return other;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1944,8 +2034,8 @@ public class Resources {
|
||||
+ " final compat is " + mCompatibilityInfo);
|
||||
}
|
||||
|
||||
clearDrawableCachesLocked(mDrawableCache, configChanges);
|
||||
clearDrawableCachesLocked(mColorDrawableCache, configChanges);
|
||||
mDrawableCache.onConfigurationChange(configChanges);
|
||||
mColorDrawableCache.onConfigurationChange(configChanges);
|
||||
mColorStateListCache.onConfigurationChange(configChanges);
|
||||
mAnimatorCache.onConfigurationChange(configChanges);
|
||||
mStateListAnimatorCache.onConfigurationChange(configChanges);
|
||||
@@ -1983,48 +2073,6 @@ public class Resources {
|
||||
return configChanges;
|
||||
}
|
||||
|
||||
private void clearDrawableCachesLocked(
|
||||
ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
|
||||
int configChanges) {
|
||||
final int N = caches.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
clearDrawableCacheLocked(caches.valueAt(i), configChanges);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearDrawableCacheLocked(
|
||||
LongSparseArray<WeakReference<ConstantState>> cache, int configChanges) {
|
||||
if (DEBUG_CONFIG) {
|
||||
Log.d(TAG, "Cleaning up drawables config changes: 0x"
|
||||
+ Integer.toHexString(configChanges));
|
||||
}
|
||||
final int N = cache.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
final WeakReference<ConstantState> ref = cache.valueAt(i);
|
||||
if (ref != null) {
|
||||
final ConstantState cs = ref.get();
|
||||
if (cs != null) {
|
||||
if (Configuration.needNewResources(
|
||||
configChanges, cs.getChangingConfigurations())) {
|
||||
if (DEBUG_CONFIG) {
|
||||
Log.d(TAG, "FLUSHING #0x"
|
||||
+ Long.toHexString(cache.keyAt(i))
|
||||
+ " / " + cs + " with changes: 0x"
|
||||
+ Integer.toHexString(cs.getChangingConfigurations()));
|
||||
}
|
||||
cache.setValueAt(i, null);
|
||||
} else if (DEBUG_CONFIG) {
|
||||
Log.d(TAG, "(Keeping #0x"
|
||||
+ Long.toHexString(cache.keyAt(i))
|
||||
+ " / " + cs + " with changes: 0x"
|
||||
+ Integer.toHexString(cs.getChangingConfigurations())
|
||||
+ ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code Locale.toLanguageTag} will transform the obsolete (and deprecated)
|
||||
* language codes "in", "ji" and "iw" to "id", "yi" and "he" respectively.
|
||||
@@ -2436,7 +2484,7 @@ public class Resources {
|
||||
}
|
||||
|
||||
final boolean isColorDrawable;
|
||||
final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches;
|
||||
final DrawableCache caches;
|
||||
final long key;
|
||||
if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
|
||||
&& value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
||||
@@ -2452,7 +2500,7 @@ public class Resources {
|
||||
// First, check whether we have a cached version of this drawable
|
||||
// that was inflated against the specified theme.
|
||||
if (!mPreloading) {
|
||||
final Drawable cachedDrawable = getCachedDrawable(caches, key, theme);
|
||||
final Drawable cachedDrawable = caches.getInstance(key, theme);
|
||||
if (cachedDrawable != null) {
|
||||
return cachedDrawable;
|
||||
}
|
||||
@@ -2479,13 +2527,8 @@ public class Resources {
|
||||
// Determine if the drawable has unresolved theme attributes. If it
|
||||
// does, we'll need to apply a theme and store it in a theme-specific
|
||||
// cache.
|
||||
final String cacheKey;
|
||||
if (!dr.canApplyTheme()) {
|
||||
cacheKey = CACHE_NOT_THEMED;
|
||||
} else if (theme == null) {
|
||||
cacheKey = CACHE_NULL_THEME;
|
||||
} else {
|
||||
cacheKey = theme.getKey();
|
||||
final boolean canApplyTheme = dr.canApplyTheme();
|
||||
if (canApplyTheme && theme != null) {
|
||||
dr = dr.mutate();
|
||||
dr.applyTheme(theme);
|
||||
dr.clearMutated();
|
||||
@@ -2495,15 +2538,14 @@ public class Resources {
|
||||
// cache: preload, not themed, null theme, or theme-specific.
|
||||
if (dr != null) {
|
||||
dr.setChangingConfigurations(value.changingConfigurations);
|
||||
cacheDrawable(value, isColorDrawable, caches, cacheKey, key, dr);
|
||||
cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
|
||||
}
|
||||
|
||||
return dr;
|
||||
}
|
||||
|
||||
private void cacheDrawable(TypedValue value, boolean isColorDrawable,
|
||||
ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
|
||||
String cacheKey, long key, Drawable dr) {
|
||||
private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches,
|
||||
Theme theme, boolean usesTheme, long key, Drawable dr) {
|
||||
final ConstantState cs = dr.getConstantState();
|
||||
if (cs == null) {
|
||||
return;
|
||||
@@ -2531,53 +2573,11 @@ public class Resources {
|
||||
}
|
||||
} else {
|
||||
synchronized (mAccessLock) {
|
||||
LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(cacheKey);
|
||||
if (themedCache == null) {
|
||||
// Clean out the caches before we add more. This shouldn't
|
||||
// happen very often.
|
||||
pruneCaches(caches);
|
||||
themedCache = new LongSparseArray<>(1);
|
||||
caches.put(cacheKey, themedCache);
|
||||
}
|
||||
themedCache.put(key, new WeakReference<>(cs));
|
||||
caches.put(key, theme, cs, usesTheme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prunes empty caches from the cache map.
|
||||
*
|
||||
* @param caches The map of caches to prune.
|
||||
*/
|
||||
private void pruneCaches(ArrayMap<String,
|
||||
LongSparseArray<WeakReference<ConstantState>>> caches) {
|
||||
final int N = caches.size();
|
||||
for (int i = N - 1; i >= 0; i--) {
|
||||
final LongSparseArray<WeakReference<ConstantState>> cache = caches.valueAt(i);
|
||||
if (pruneCache(cache)) {
|
||||
caches.removeAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prunes obsolete weak references from a cache, returning {@code true} if
|
||||
* the cache is empty and should be removed.
|
||||
*
|
||||
* @param cache The cache of weak references to prune.
|
||||
* @return {@code true} if the cache is empty and should be removed.
|
||||
*/
|
||||
private boolean pruneCache(LongSparseArray<WeakReference<ConstantState>> cache) {
|
||||
final int N = cache.size();
|
||||
for (int i = N - 1; i >= 0; i--) {
|
||||
final WeakReference entry = cache.valueAt(i);
|
||||
if (entry == null || entry.get() == null) {
|
||||
cache.removeAt(i);
|
||||
}
|
||||
}
|
||||
return cache.size() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a drawable from XML or resources stream.
|
||||
*/
|
||||
@@ -2631,51 +2631,6 @@ public class Resources {
|
||||
return dr;
|
||||
}
|
||||
|
||||
private Drawable getCachedDrawable(
|
||||
ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
|
||||
long key, Theme theme) {
|
||||
synchronized (mAccessLock) {
|
||||
// First search theme-agnostic cache.
|
||||
final Drawable unthemedDrawable = getCachedDrawableLocked(
|
||||
caches, key, CACHE_NOT_THEMED);
|
||||
if (unthemedDrawable != null) {
|
||||
return unthemedDrawable;
|
||||
}
|
||||
|
||||
// Next search theme-specific cache.
|
||||
final String themeKey = theme != null ? theme.getKey() : CACHE_NULL_THEME;
|
||||
return getCachedDrawableLocked(caches, key, themeKey);
|
||||
}
|
||||
}
|
||||
|
||||
private Drawable getCachedDrawableLocked(
|
||||
ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
|
||||
long key, String themeKey) {
|
||||
final LongSparseArray<WeakReference<ConstantState>> cache = caches.get(themeKey);
|
||||
if (cache != null) {
|
||||
final ConstantState entry = getConstantStateLocked(cache, key);
|
||||
if (entry != null) {
|
||||
return entry.newDrawable(this);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ConstantState getConstantStateLocked(
|
||||
LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) {
|
||||
final WeakReference<ConstantState> wr = drawableCache.get(key);
|
||||
if (wr != null) {
|
||||
final ConstantState entry = wr.get();
|
||||
if (entry != null) {
|
||||
return entry;
|
||||
} else {
|
||||
// Our entry has been purged.
|
||||
drawableCache.delete(key);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
ColorStateList loadColorStateList(TypedValue value, int id, Theme theme)
|
||||
throws NotFoundException {
|
||||
@@ -2713,8 +2668,7 @@ public class Resources {
|
||||
}
|
||||
|
||||
final ConfigurationBoundResourceCache<ColorStateList> cache = mColorStateListCache;
|
||||
|
||||
csl = cache.get(key, theme);
|
||||
csl = cache.getInstance(key, theme);
|
||||
if (csl != null) {
|
||||
return csl;
|
||||
}
|
||||
|
||||
233
core/java/android/content/res/ThemedResourceCache.java
Normal file
233
core/java/android/content/res/ThemedResourceCache.java
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.content.res.Resources.ThemeKey;
|
||||
import android.util.LongSparseArray;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* Data structure used for caching data against themes.
|
||||
*
|
||||
* @param <T> type of data to cache
|
||||
*/
|
||||
abstract class ThemedResourceCache<T> {
|
||||
private ArrayMap<ThemeKey, LongSparseArray<WeakReference<T>>> mThemedEntries;
|
||||
private LongSparseArray<WeakReference<T>> mUnthemedEntries;
|
||||
private LongSparseArray<WeakReference<T>> mNullThemedEntries;
|
||||
|
||||
/**
|
||||
* Adds a new theme-dependent entry to the cache.
|
||||
*
|
||||
* @param key a key that uniquely identifies the entry
|
||||
* @param theme the theme against which this entry was inflated, or
|
||||
* {@code null} if the entry has no theme applied
|
||||
* @param entry the entry to cache
|
||||
*/
|
||||
public void put(long key, @Nullable Theme theme, @NonNull T entry) {
|
||||
put(key, theme, entry, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new entry to the cache.
|
||||
*
|
||||
* @param key a key that uniquely identifies the entry
|
||||
* @param theme the theme against which this entry was inflated, or
|
||||
* {@code null} if the entry has no theme applied
|
||||
* @param entry the entry to cache
|
||||
* @param usesTheme {@code true} if the entry is affected theme changes,
|
||||
* {@code false} otherwise
|
||||
*/
|
||||
public void put(long key, @Nullable Theme theme, @NonNull T entry, boolean usesTheme) {
|
||||
if (entry == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
final LongSparseArray<WeakReference<T>> entries;
|
||||
if (!usesTheme) {
|
||||
entries = getUnthemedLocked(true);
|
||||
} else {
|
||||
entries = getThemedLocked(theme, true);
|
||||
}
|
||||
if (entries != null) {
|
||||
entries.put(key, new WeakReference<>(entry));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an entry from the cache.
|
||||
*
|
||||
* @param key a key that uniquely identifies the entry
|
||||
* @param theme the theme where the entry will be used
|
||||
* @return a cached entry, or {@code null} if not in the cache
|
||||
*/
|
||||
@Nullable
|
||||
public T get(long key, @Nullable Theme theme) {
|
||||
// The themed (includes null-themed) and unthemed caches are mutually
|
||||
// exclusive, so we'll give priority to whichever one we think we'll
|
||||
// hit first. Since most of the framework drawables are themed, that's
|
||||
// probably going to be the themed cache.
|
||||
synchronized (this) {
|
||||
final LongSparseArray<WeakReference<T>> themedEntries = getThemedLocked(theme, false);
|
||||
if (themedEntries != null) {
|
||||
final WeakReference<T> themedEntry = themedEntries.get(key);
|
||||
if (themedEntry != null) {
|
||||
return themedEntry.get();
|
||||
}
|
||||
}
|
||||
|
||||
final LongSparseArray<WeakReference<T>> unthemedEntries = getUnthemedLocked(false);
|
||||
if (unthemedEntries != null) {
|
||||
final WeakReference<T> unthemedEntry = unthemedEntries.get(key);
|
||||
if (unthemedEntry != null) {
|
||||
return unthemedEntry.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prunes cache entries that have been invalidated by a configuration
|
||||
* change.
|
||||
*
|
||||
* @param configChanges a bitmask of configuration changes
|
||||
*/
|
||||
public void onConfigurationChange(int configChanges) {
|
||||
prune(configChanges);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a cached entry has been invalidated by a configuration
|
||||
* change.
|
||||
*
|
||||
* @param entry a cached entry
|
||||
* @param configChanges a non-zero bitmask of configuration changes
|
||||
* @return {@code true} if the entry is invalid, {@code false} otherwise
|
||||
*/
|
||||
protected abstract boolean shouldInvalidateEntry(@NonNull T entry, int configChanges);
|
||||
|
||||
/**
|
||||
* Returns the cached data for the specified theme, optionally creating a
|
||||
* new entry if one does not already exist.
|
||||
*
|
||||
* @param t the theme for which to return cached data
|
||||
* @param create {@code true} to create an entry if one does not already
|
||||
* exist, {@code false} otherwise
|
||||
* @return the cached data for the theme, or {@code null} if the cache is
|
||||
* empty and {@code create} was {@code false}
|
||||
*/
|
||||
@Nullable
|
||||
private LongSparseArray<WeakReference<T>> getThemedLocked(@Nullable Theme t, boolean create) {
|
||||
if (t == null) {
|
||||
if (mNullThemedEntries == null && create) {
|
||||
mNullThemedEntries = new LongSparseArray<>(1);
|
||||
}
|
||||
return mNullThemedEntries;
|
||||
}
|
||||
|
||||
if (mThemedEntries == null) {
|
||||
if (create) {
|
||||
mThemedEntries = new ArrayMap<>(1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final ThemeKey key = t.getKey();
|
||||
LongSparseArray<WeakReference<T>> cache = mThemedEntries.get(key);
|
||||
if (cache == null && create) {
|
||||
cache = new LongSparseArray<>(1);
|
||||
|
||||
final ThemeKey keyClone = key.clone();
|
||||
mThemedEntries.put(keyClone, cache);
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the theme-agnostic cached data.
|
||||
*
|
||||
* @param create {@code true} to create an entry if one does not already
|
||||
* exist, {@code false} otherwise
|
||||
* @return the theme-agnostic cached data, or {@code null} if the cache is
|
||||
* empty and {@code create} was {@code false}
|
||||
*/
|
||||
@Nullable
|
||||
private LongSparseArray<WeakReference<T>> getUnthemedLocked(boolean create) {
|
||||
if (mUnthemedEntries == null && create) {
|
||||
mUnthemedEntries = new LongSparseArray<>(1);
|
||||
}
|
||||
return mUnthemedEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prunes cache entries affected by configuration changes or where weak
|
||||
* references have expired.
|
||||
*
|
||||
* @param configChanges a bitmask of configuration changes, or {@code 0} to
|
||||
* simply prune missing weak references
|
||||
* @return {@code true} if the cache is completely empty after pruning
|
||||
*/
|
||||
private boolean prune(int configChanges) {
|
||||
synchronized (this) {
|
||||
if (mThemedEntries != null) {
|
||||
for (int i = mThemedEntries.size() - 1; i >= 0; i--) {
|
||||
if (pruneEntriesLocked(mThemedEntries.valueAt(i), configChanges)) {
|
||||
mThemedEntries.removeAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pruneEntriesLocked(mNullThemedEntries, configChanges);
|
||||
pruneEntriesLocked(mUnthemedEntries, configChanges);
|
||||
|
||||
return mThemedEntries == null && mNullThemedEntries == null
|
||||
&& mUnthemedEntries == null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean pruneEntriesLocked(@Nullable LongSparseArray<WeakReference<T>> entries,
|
||||
int configChanges) {
|
||||
if (entries == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = entries.size() - 1; i >= 0; i--) {
|
||||
final WeakReference<T> ref = entries.valueAt(i);
|
||||
if (ref == null || pruneEntryLocked(ref.get(), configChanges)) {
|
||||
entries.removeAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
return entries.size() == 0;
|
||||
}
|
||||
|
||||
private boolean pruneEntryLocked(@Nullable T entry, int configChanges) {
|
||||
return entry == null || (configChanges != 0
|
||||
&& shouldInvalidateEntry(entry, configChanges));
|
||||
}
|
||||
}
|
||||
@@ -976,6 +976,12 @@ static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz,
|
||||
dest->setTo(*src);
|
||||
}
|
||||
|
||||
static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle)
|
||||
{
|
||||
ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
|
||||
theme->clear();
|
||||
}
|
||||
|
||||
static jint android_content_AssetManager_loadThemeAttributeValue(
|
||||
JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve)
|
||||
{
|
||||
@@ -2108,6 +2114,8 @@ static JNINativeMethod gAssetManagerMethods[] = {
|
||||
(void*) android_content_AssetManager_applyThemeStyle },
|
||||
{ "copyTheme", "(JJ)V",
|
||||
(void*) android_content_AssetManager_copyTheme },
|
||||
{ "clearTheme", "(J)V",
|
||||
(void*) android_content_AssetManager_clearTheme },
|
||||
{ "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I",
|
||||
(void*) android_content_AssetManager_loadThemeAttributeValue },
|
||||
{ "getThemeChangingConfigurations", "(J)I",
|
||||
|
||||
@@ -1631,6 +1631,7 @@ public:
|
||||
|
||||
status_t applyStyle(uint32_t resID, bool force=false);
|
||||
status_t setTo(const Theme& other);
|
||||
status_t clear();
|
||||
|
||||
/**
|
||||
* Retrieve a value in the theme. If the theme defines this
|
||||
|
||||
@@ -3336,6 +3336,30 @@ status_t ResTable::Theme::setTo(const Theme& other)
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t ResTable::Theme::clear()
|
||||
{
|
||||
if (kDebugTableTheme) {
|
||||
ALOGI("Clearing theme %p...\n", this);
|
||||
dumpToLog();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < Res_MAXPACKAGE; i++) {
|
||||
if (mPackages[i] != NULL) {
|
||||
free_package(mPackages[i]);
|
||||
mPackages[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
mTypeSpecFlags = 0;
|
||||
|
||||
if (kDebugTableTheme) {
|
||||
ALOGI("Final theme:");
|
||||
dumpToLog();
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue,
|
||||
uint32_t* outTypeSpecFlags) const
|
||||
{
|
||||
|
||||
@@ -1138,7 +1138,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
|
||||
|
||||
public GlobalActionsDialog(Context context, AlertParams params) {
|
||||
super(context, getDialogTheme(context));
|
||||
mContext = context;
|
||||
mContext = getContext();
|
||||
mAlert = new AlertController(mContext, this, getWindow());
|
||||
mAdapter = (MyAdapter) params.mAdapter;
|
||||
mWindowTouchSlop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.FactoryTest;
|
||||
@@ -291,7 +292,7 @@ public final class SystemServer {
|
||||
private void createSystemContext() {
|
||||
ActivityThread activityThread = ActivityThread.systemMain();
|
||||
mSystemContext = activityThread.getSystemContext();
|
||||
mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
|
||||
mSystemContext.setTheme(android.R.style.Theme_Material_DayNight_DarkActionBar);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1026,6 +1027,12 @@ public final class SystemServer {
|
||||
w.getDefaultDisplay().getMetrics(metrics);
|
||||
context.getResources().updateConfiguration(config, metrics);
|
||||
|
||||
// The system context's theme may be configuration-dependent.
|
||||
final Theme systemTheme = context.getTheme();
|
||||
if (systemTheme.getChangingConfigurations() != 0) {
|
||||
systemTheme.rebase();
|
||||
}
|
||||
|
||||
try {
|
||||
// TODO: use boot phase
|
||||
mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
|
||||
|
||||
Reference in New Issue
Block a user