Ensure no duplicates in ThemeKey
Some apps keep adding the same resources to their theme key
objects on e.g. switching dark mode. Need to be prepared to
limit the size of the arrays, otherwise theme switching gets
slower with each toggle
+ a bit more efficient native handling
Bug: 242005877
Test: manual, 10k of theme switches with no noticable slowdown
Change-Id: Icf74770bd41ebeb0a31f527ae3616de00f23b0ae
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index a03286d3..fb1fcf8 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -173,6 +173,7 @@
* mThemeRefNextFlushSize is reached.
*/
private static final int MIN_THEME_REFS_FLUSH_SIZE = 32;
+ private static final int MAX_THEME_REFS_FLUSH_SIZE = 512;
private int mThemeRefsNextFlushSize = MIN_THEME_REFS_FLUSH_SIZE;
private int mBaseApkAssetsSize;
@@ -364,10 +365,10 @@
// Rebase the ThemeImpls using the new ResourcesImpl.
synchronized (mThemeRefs) {
+ cleanupThemeReferences();
final int count = mThemeRefs.size();
for (int i = 0; i < count; i++) {
- WeakReference<Theme> weakThemeRef = mThemeRefs.get(i);
- Theme theme = weakThemeRef != null ? weakThemeRef.get() : null;
+ Theme theme = mThemeRefs.get(i).get();
if (theme != null) {
theme.rebase(mResourcesImpl);
}
@@ -2001,6 +2002,15 @@
private int mHashCode = 0;
+ private boolean containsValue(int resId, boolean force) {
+ for (int i = 0; i < mCount; ++i) {
+ if (mResId[i] == resId && mForce[i] == force) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public void append(int resId, boolean force) {
if (mResId == null) {
mResId = new int[4];
@@ -2010,6 +2020,11 @@
mForce = new boolean[4];
}
+ // Some apps tend to keep adding same resources over and over, let's protect from it.
+ if (containsValue(resId, force)) {
+ return;
+ }
+
mResId = GrowingArrayUtils.append(mResId, mCount, resId);
mForce = GrowingArrayUtils.append(mForce, mCount, force);
mCount++;
@@ -2073,6 +2088,19 @@
}
}
+ static int nextPowerOf2(int number) {
+ return number < 2 ? 2 : 1 >> ((int) (Math.log(number - 1) / Math.log(2)) + 1);
+ }
+
+ private void cleanupThemeReferences() {
+ // Clean up references to garbage collected themes
+ if (mThemeRefs.size() > mThemeRefsNextFlushSize) {
+ mThemeRefs.removeIf(ref -> ref.refersTo(null));
+ mThemeRefsNextFlushSize = Math.min(Math.max(MIN_THEME_REFS_FLUSH_SIZE,
+ nextPowerOf2(mThemeRefs.size())), MAX_THEME_REFS_FLUSH_SIZE);
+ }
+ }
+
/**
* Generate a new Theme object for this set of Resources. It initially
* starts out empty.
@@ -2083,14 +2111,8 @@
Theme theme = new Theme();
theme.setImpl(mResourcesImpl.newThemeImpl());
synchronized (mThemeRefs) {
+ cleanupThemeReferences();
mThemeRefs.add(new WeakReference<>(theme));
-
- // Clean up references to garbage collected themes
- if (mThemeRefs.size() > mThemeRefsNextFlushSize) {
- mThemeRefs.removeIf(ref -> ref.refersTo(null));
- mThemeRefsNextFlushSize = Math.max(MIN_THEME_REFS_FLUSH_SIZE,
- 2 * mThemeRefs.size());
- }
}
return theme;
}
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 235700b..1381bdd 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -1068,7 +1068,7 @@
base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(uint32_t resid) const {
std::vector<uint32_t> found_resids;
const auto bag = GetBag(resid, found_resids);
- cached_bag_resid_stacks_.emplace(resid, found_resids);
+ cached_bag_resid_stacks_.emplace(resid, std::move(found_resids));
return bag;
}
@@ -1468,7 +1468,6 @@
continue;
}
- Theme::Entry new_entry{attr_res_id, it->cookie, (*bag)->type_spec_flags, it->value};
auto entry_it = std::lower_bound(entries_.begin(), entries_.end(), attr_res_id,
ThemeEntryKeyComparer{});
if (entry_it != entries_.end() && entry_it->attr_res_id == attr_res_id) {
@@ -1477,10 +1476,10 @@
/// true.
entries_.erase(entry_it);
} else if (force) {
- *entry_it = new_entry;
+ *entry_it = Entry{attr_res_id, it->cookie, (*bag)->type_spec_flags, it->value};
}
} else {
- entries_.insert(entry_it, new_entry);
+ entries_.insert(entry_it, Entry{attr_res_id, it->cookie, (*bag)->type_spec_flags, it->value});
}
}
return {};