Merge "Fix drawable-state cache expired issue" into main

This commit is contained in:
Brandon Liu
2023-07-27 17:01:56 +00:00
committed by Gerrit Code Review
3 changed files with 117 additions and 94 deletions

View File

@@ -53,6 +53,7 @@ import android.graphics.drawable.Drawable.ConstantState;
import android.graphics.drawable.DrawableInflater;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AttributeSet;
@@ -132,6 +133,11 @@ public class Resources {
private static final Object sSync = new Object();
private final Object mUpdateLock = new Object();
/**
* Controls whether we should preload resources during zygote init.
*/
private static final boolean PRELOAD_RESOURCES = true;
// Used by BridgeResources in layoutlib
@UnsupportedAppUsage
static Resources mSystem = null;
@@ -2666,6 +2672,98 @@ public class Resources {
}
}
/**
* Load in commonly used resources, so they can be shared across processes.
*
* These tend to be a few Kbytes, but are frequently in the 20-40K range, and occasionally even
* larger.
* @hide
*/
@UnsupportedAppUsage
public static void preloadResources() {
try {
final Resources sysRes = Resources.getSystem();
sysRes.startPreloading();
if (PRELOAD_RESOURCES) {
Log.i(TAG, "Preloading resources...");
long startTime = SystemClock.uptimeMillis();
TypedArray ar = sysRes.obtainTypedArray(
com.android.internal.R.array.preloaded_drawables);
int numberOfEntries = preloadDrawables(sysRes, ar);
ar.recycle();
Log.i(TAG, "...preloaded " + numberOfEntries + " resources in "
+ (SystemClock.uptimeMillis() - startTime) + "ms.");
startTime = SystemClock.uptimeMillis();
ar = sysRes.obtainTypedArray(
com.android.internal.R.array.preloaded_color_state_lists);
numberOfEntries = preloadColorStateLists(sysRes, ar);
ar.recycle();
Log.i(TAG, "...preloaded " + numberOfEntries + " resources in "
+ (SystemClock.uptimeMillis() - startTime) + "ms.");
if (sysRes.getBoolean(
com.android.internal.R.bool.config_freeformWindowManagement)) {
startTime = SystemClock.uptimeMillis();
ar = sysRes.obtainTypedArray(
com.android.internal.R.array.preloaded_freeform_multi_window_drawables);
numberOfEntries = preloadDrawables(sysRes, ar);
ar.recycle();
Log.i(TAG, "...preloaded " + numberOfEntries + " resource in "
+ (SystemClock.uptimeMillis() - startTime) + "ms.");
}
}
sysRes.finishPreloading();
} catch (RuntimeException e) {
Log.w(TAG, "Failure preloading resources", e);
}
}
private static int preloadColorStateLists(Resources resources, TypedArray ar) {
final int numberOfEntries = ar.length();
for (int i = 0; i < numberOfEntries; i++) {
int id = ar.getResourceId(i, 0);
if (id != 0) {
if (resources.getColorStateList(id, null) == null) {
throw new IllegalArgumentException(
"Unable to find preloaded color resource #0x"
+ Integer.toHexString(id)
+ " (" + ar.getString(i) + ")");
}
}
}
return numberOfEntries;
}
private static int preloadDrawables(Resources resources, TypedArray ar) {
final int numberOfEntries = ar.length();
for (int i = 0; i < numberOfEntries; i++) {
int id = ar.getResourceId(i, 0);
if (id != 0) {
if (resources.getDrawable(id, null) == null) {
throw new IllegalArgumentException(
"Unable to find preloaded drawable resource #0x"
+ Integer.toHexString(id)
+ " (" + ar.getString(i) + ")");
}
}
}
return numberOfEntries;
}
/**
* Clear the cache when the framework resources packages is changed.
* @hide
*/
@VisibleForTesting
public static void resetPreloadDrawableStateCache() {
ResourcesImpl.resetDrawableStateCache();
preloadResources();
}
/** @hide */
public void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "class=" + getClass());

View File

@@ -52,6 +52,7 @@ import android.util.TypedValue;
import android.util.Xml;
import android.view.DisplayAdjustments;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.GrowingArrayUtils;
import libcore.util.NativeAllocationRegistry;
@@ -158,6 +159,23 @@ public class ResourcesImpl {
sPreloadedDrawables[1] = new LongSparseArray<>();
}
/**
* Clear the cache when the framework resources packages is changed.
*
* It's only used in the test initial function instead of regular app behaviors. It doesn't
* guarantee the thread-safety so mark this with @VisibleForTesting.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
static void resetDrawableStateCache() {
synchronized (sSync) {
sPreloadedDrawables[0].clear();
sPreloadedDrawables[1].clear();
sPreloadedColorDrawables.clear();
sPreloadedComplexColors.clear();
sPreloaded = false;
}
}
/**
* Creates a new ResourcesImpl object with CompatibilityInfo.
*

View File

@@ -26,7 +26,6 @@ import android.app.ApplicationLoaders;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.SharedLibraryInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Environment;
import android.os.IInstalld;
@@ -101,22 +100,11 @@ public class ZygoteInit {
// --usap-socket-name parameter.
private static final String SOCKET_NAME_ARG = "--socket-name=";
/**
* Used to pre-load resources.
*/
@UnsupportedAppUsage
private static Resources mResources;
/**
* The path of a file that contains classes to preload.
*/
private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
/**
* Controls whether we should preload resources during zygote init.
*/
private static final boolean PRELOAD_RESOURCES = true;
private static final int UNPRIVILEGED_UID = 9999;
private static final int UNPRIVILEGED_GID = 9999;
@@ -143,7 +131,7 @@ public class ZygoteInit {
cacheNonBootClasspathClassLoaders();
bootTimingsTraceLog.traceEnd(); // CacheNonBootClasspathClassLoaders
bootTimingsTraceLog.traceBegin("PreloadResources");
preloadResources();
Resources.preloadResources();
bootTimingsTraceLog.traceEnd(); // PreloadResources
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadAppProcessHALs");
nativePreloadAppProcessHALs();
@@ -413,87 +401,6 @@ public class ZygoteInit {
});
}
/**
* Load in commonly used resources, so they can be shared across processes.
*
* These tend to be a few Kbytes, but are frequently in the 20-40K range, and occasionally even
* larger.
*/
private static void preloadResources() {
try {
mResources = Resources.getSystem();
mResources.startPreloading();
if (PRELOAD_RESOURCES) {
Log.i(TAG, "Preloading resources...");
long startTime = SystemClock.uptimeMillis();
TypedArray ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_drawables);
int N = preloadDrawables(ar);
ar.recycle();
Log.i(TAG, "...preloaded " + N + " resources in "
+ (SystemClock.uptimeMillis() - startTime) + "ms.");
startTime = SystemClock.uptimeMillis();
ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_color_state_lists);
N = preloadColorStateLists(ar);
ar.recycle();
Log.i(TAG, "...preloaded " + N + " resources in "
+ (SystemClock.uptimeMillis() - startTime) + "ms.");
if (mResources.getBoolean(
com.android.internal.R.bool.config_freeformWindowManagement)) {
startTime = SystemClock.uptimeMillis();
ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_freeform_multi_window_drawables);
N = preloadDrawables(ar);
ar.recycle();
Log.i(TAG, "...preloaded " + N + " resource in "
+ (SystemClock.uptimeMillis() - startTime) + "ms.");
}
}
mResources.finishPreloading();
} catch (RuntimeException e) {
Log.w(TAG, "Failure preloading resources", e);
}
}
private static int preloadColorStateLists(TypedArray ar) {
int N = ar.length();
for (int i = 0; i < N; i++) {
int id = ar.getResourceId(i, 0);
if (id != 0) {
if (mResources.getColorStateList(id, null) == null) {
throw new IllegalArgumentException(
"Unable to find preloaded color resource #0x"
+ Integer.toHexString(id)
+ " (" + ar.getString(i) + ")");
}
}
}
return N;
}
private static int preloadDrawables(TypedArray ar) {
int N = ar.length();
for (int i = 0; i < N; i++) {
int id = ar.getResourceId(i, 0);
if (id != 0) {
if (mResources.getDrawable(id, null) == null) {
throw new IllegalArgumentException(
"Unable to find preloaded drawable resource #0x"
+ Integer.toHexString(id)
+ " (" + ar.getString(i) + ")");
}
}
}
return N;
}
/**
* Runs several special GCs to try to clean up a few generations of softly- and final-reachable
* objects, along with any other garbage. This is only useful just before a fork().