Fix the memory leak issue in AppCompatDelegate.getLocaleManagerForApplication()
1. In AppLocalesStorageHelper.syncLocalesToFramework(), using context to get the LocaleManager service then call setApplicationLocales() to sync locales directly.
2. Remove sAppContext and sLocaleManager in
AppCompatDelegate.getLocaleManagerForApplication() to avoid the
memory leak issue.
Test: manual test
Bug: 260322829
Change-Id: I34f0de9fd5f6d3f533ba29ebeff360584840e1fa
(cherry picked from commit 92a0edef1bc865e86f72e204065cc8ac15e11a75)
Merged-In: I34f0de9fd5f6d3f533ba29ebeff360584840e1fa
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegate.java b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegate.java
index b6944ba..627e63e 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegate.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegate.java
@@ -189,8 +189,6 @@
private static LocaleListCompat sStoredAppLocales = null;
private static Boolean sIsAutoStoreLocalesOptedIn = null;
private static boolean sIsFrameworkSyncChecked = false;
- private static Object sLocaleManager = null;
- private static Context sAppContext = null;
/**
* All AppCompatDelegate instances associated with a "live" Activity, e.g. lifecycle state is
@@ -835,30 +833,16 @@
*/
@RequiresApi(33)
static Object getLocaleManagerForApplication() {
- if (sLocaleManager != null) {
- return sLocaleManager;
- }
- // Traversing through the active delegates to retrieve context for any one non null
- // delegate.
- // This context is used to create a localeManager which is saved as a static variable to
- // reduce multiple object creation for different activities.
- if (sAppContext == null) {
- for (WeakReference<AppCompatDelegate> activeDelegate : sActivityDelegates) {
- final AppCompatDelegate delegate = activeDelegate.get();
- if (delegate != null) {
- Context context = delegate.getContextForDelegate();
- if (context != null) {
- sAppContext = context;
- break;
- }
+ for (WeakReference<AppCompatDelegate> activeDelegate : sActivityDelegates) {
+ final AppCompatDelegate delegate = activeDelegate.get();
+ if (delegate != null) {
+ Context context = delegate.getContextForDelegate();
+ if (context != null) {
+ return context.getSystemService(Context.LOCALE_SERVICE);
}
}
}
-
- if (sAppContext != null) {
- sLocaleManager = sAppContext.getSystemService(Context.LOCALE_SERVICE);
- }
- return sLocaleManager;
+ return null;
}
/**
@@ -955,15 +939,6 @@
}
}
-
- /**
- * Sets the value for {@link AppCompatDelegate#sAppContext} which is the context for the
- * current application.
- */
- static void setAppContext(Context context) {
- sAppContext = context;
- }
-
/**
* Sets whether vector drawables on older platforms (< API 21) can be used within
* {@link android.graphics.drawable.DrawableContainer} resources.
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppLocalesStorageHelper.java b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppLocalesStorageHelper.java
index d30f599..8c75ca2 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppLocalesStorageHelper.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppLocalesStorageHelper.java
@@ -23,11 +23,11 @@
import android.content.ComponentName;
import android.content.Context;
+import android.os.Build;
import android.util.Log;
import android.util.Xml;
import androidx.annotation.NonNull;
-import androidx.core.os.LocaleListCompat;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -163,36 +163,41 @@
* this method will throw an error.</p>
*/
static void syncLocalesToFramework(Context context) {
- ComponentName app_locales_component = new ComponentName(
- context, APP_LOCALES_META_DATA_HOLDER_SERVICE_NAME);
+ if (Build.VERSION.SDK_INT >= 33) {
+ ComponentName app_locales_component = new ComponentName(
+ context, APP_LOCALES_META_DATA_HOLDER_SERVICE_NAME);
- if (context.getPackageManager().getComponentEnabledSetting(app_locales_component)
- != COMPONENT_ENABLED_STATE_ENABLED) {
- AppCompatDelegate.setAppContext(context);
- // ComponentEnabledSetting for the app component app_locales_component is used as a
- // marker to represent that the locales has been synced from AndroidX to framework
- // If this marker is found in ENABLED state then we do not need to sync again.
- if (getApplicationLocales().isEmpty()) {
- // We check if some locales are applied by the framework or not (this is done to
- // ensure that we don't overwrite newer locales set by the framework). If no
- // app-locales are found then we need to sync the app-specific locales from androidX
- // to framework.
+ if (context.getPackageManager().getComponentEnabledSetting(app_locales_component)
+ != COMPONENT_ENABLED_STATE_ENABLED) {
+ // ComponentEnabledSetting for the app component app_locales_component is used as a
+ // marker to represent that the locales has been synced from AndroidX to framework
+ // If this marker is found in ENABLED state then we do not need to sync again.
+ if (getApplicationLocales().isEmpty()) {
+ // We check if some locales are applied by the framework or not (this is done to
+ // ensure that we don't overwrite newer locales set by the framework). If no
+ // app-locales are found then we need to sync the app-specific locales from
+ // androidX to framework.
- String appLocales = readLocales(context);
- // if locales are present in storage, call the setApplicationLocales() API. As the
- // API version is >= 33, this call will be directed to the framework API and the
- // locales will be persisted there.
- AppCompatDelegate.setApplicationLocales(
- LocaleListCompat.forLanguageTags(appLocales));
+ String appLocales = readLocales(context);
+ // if locales are present in storage, call the setApplicationLocales() API. As
+ // the API version is >= 33, this call will be directed to the framework API and
+ // the locales will be persisted there.
+ Object localeManager = context.getSystemService(Context.LOCALE_SERVICE);
+ if (localeManager != null) {
+ AppCompatDelegate.Api33Impl.localeManagerSetApplicationLocales(
+ localeManager,
+ AppCompatDelegate.Api24Impl.localeListForLanguageTags(appLocales));
+ }
+ }
+ // setting ComponentEnabledSetting for app component using
+ // AppLocalesMetadataHolderService (used only for locales, thus minimizing
+ // the chances of conflicts). Setting it as ENABLED marks the success of app-locales
+ // sync from AndroidX to framework.
+ // Flag DONT_KILL_APP indicates that you don't want to kill the app containing the
+ // component.
+ context.getPackageManager().setComponentEnabledSetting(app_locales_component,
+ COMPONENT_ENABLED_STATE_ENABLED, /* flags= */ DONT_KILL_APP);
}
- // setting ComponentEnabledSetting for app component using
- // AppLocalesMetadataHolderService (used only for locales, thus minimizing
- // the chances of conflicts). Setting it as ENABLED marks the success of app-locales
- // sync from AndroidX to framework.
- // Flag DONT_KILL_APP indicates that you don't want to kill the app containing the
- // component.
- context.getPackageManager().setComponentEnabledSetting(app_locales_component,
- COMPONENT_ENABLED_STATE_ENABLED, /* flags= */ DONT_KILL_APP);
}
}