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:
Alan Viverette
2015-05-07 21:40:23 +00:00
committed by Android Git Automerger
12 changed files with 501 additions and 290 deletions

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
}
}
}
}

View 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;
}
}

View File

@@ -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;
}

View 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));
}
}

View File

@@ -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",

View File

@@ -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

View File

@@ -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
{

View File

@@ -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();

View File

@@ -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());