Merge "Fix drawable-state cache expired issue" into main am: 3ba29fc4e8

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2437312

Change-Id: Ia11517a53200e6010c014b890955976b66bcd001
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Brandon Liu
2023-07-27 18:25:50 +00:00
committed by Automerger Merge Worker
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.graphics.drawable.DrawableInflater;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.SystemClock;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.AttributeSet; import android.util.AttributeSet;
@@ -132,6 +133,11 @@ public class Resources {
private static final Object sSync = new Object(); private static final Object sSync = new Object();
private final Object mUpdateLock = 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 // Used by BridgeResources in layoutlib
@UnsupportedAppUsage @UnsupportedAppUsage
static Resources mSystem = null; 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 */ /** @hide */
public void dump(PrintWriter pw, String prefix) { public void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "class=" + getClass()); pw.println(prefix + "class=" + getClass());

View File

@@ -52,6 +52,7 @@ import android.util.TypedValue;
import android.util.Xml; import android.util.Xml;
import android.view.DisplayAdjustments; import android.view.DisplayAdjustments;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.GrowingArrayUtils; import com.android.internal.util.GrowingArrayUtils;
import libcore.util.NativeAllocationRegistry; import libcore.util.NativeAllocationRegistry;
@@ -158,6 +159,23 @@ public class ResourcesImpl {
sPreloadedDrawables[1] = new LongSparseArray<>(); 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. * Creates a new ResourcesImpl object with CompatibilityInfo.
* *

View File

@@ -26,7 +26,6 @@ import android.app.ApplicationLoaders;
import android.compat.annotation.UnsupportedAppUsage; import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.SharedLibraryInfo; import android.content.pm.SharedLibraryInfo;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Build; import android.os.Build;
import android.os.Environment; import android.os.Environment;
import android.os.IInstalld; import android.os.IInstalld;
@@ -101,22 +100,11 @@ public class ZygoteInit {
// --usap-socket-name parameter. // --usap-socket-name parameter.
private static final String SOCKET_NAME_ARG = "--socket-name="; 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. * The path of a file that contains classes to preload.
*/ */
private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes"; 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_UID = 9999;
private static final int UNPRIVILEGED_GID = 9999; private static final int UNPRIVILEGED_GID = 9999;
@@ -143,7 +131,7 @@ public class ZygoteInit {
cacheNonBootClasspathClassLoaders(); cacheNonBootClasspathClassLoaders();
bootTimingsTraceLog.traceEnd(); // CacheNonBootClasspathClassLoaders bootTimingsTraceLog.traceEnd(); // CacheNonBootClasspathClassLoaders
bootTimingsTraceLog.traceBegin("PreloadResources"); bootTimingsTraceLog.traceBegin("PreloadResources");
preloadResources(); Resources.preloadResources();
bootTimingsTraceLog.traceEnd(); // PreloadResources bootTimingsTraceLog.traceEnd(); // PreloadResources
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadAppProcessHALs"); Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadAppProcessHALs");
nativePreloadAppProcessHALs(); 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 * 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(). * objects, along with any other garbage. This is only useful just before a fork().