diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 9b169499b41fc..c8dbfc9148476 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -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()); diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 3bb237ac12287..480e43e2f5d11 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -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. * diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 85cb15bdd9061..b3938b4b33fbe 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -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().