From ccb024aa2b985ddc7f65b53191a84f1891f31cf2 Mon Sep 17 00:00:00 2001 From: Yohei Yukawa Date: Mon, 13 Jun 2016 22:16:52 -0700 Subject: Quick workaround for a performance regression in IME APIs. It turns out that the performance of InputMethodManager#getCurrentInputMethodSubtype() is regressed from ~1ms to ~20ms when - Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE == -1 and - The active IME supports many subtypes (~100) because we try to find a fallback subtype based on the system locales every time when IMM#getCurrentInputMethodSubtype() is called. This could be contributing UI janks because spell checker clients running in the UI thread indirectly depend on that method. Fortunatelly the critical path is in InputMethodUtils#getImplicitlyApplicableSubtypesLockedImpl(), which is basically a state-less method. We can easily and safely cache its result by using LocaleList and InputMethod as cache keys. With this CL the performance basically recovers to the Android M level. Bug: 28889203 Change-Id: I5ed16c7f14cc18052854f4fd6c9bae8550c332ee --- .../internal/inputmethod/InputMethodUtils.java | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java index a028449e39d7..716997f815dc 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java +++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java @@ -41,6 +41,7 @@ import android.view.inputmethod.InputMethodSubtype; import android.view.textservice.SpellCheckerInfo; import android.view.textservice.TextServicesManager; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; @@ -83,6 +84,17 @@ public class InputMethodUtils { Locale.UK, // "en_GB" }; + // A temporary workaround for the performance concerns in + // #getImplicitlyApplicableSubtypesLocked(Resources, InputMethodInfo). + // TODO: Optimize all the critical paths including this one. + private static final Object sCacheLock = new Object(); + @GuardedBy("sCacheLock") + private static LocaleList sCachedSystemLocales; + @GuardedBy("sCacheLock") + private static InputMethodInfo sCachedInputMethodInfo; + @GuardedBy("sCacheLock") + private static ArrayList sCachedResult; + private InputMethodUtils() { // This utility class is not publicly instantiable. } @@ -498,6 +510,32 @@ public class InputMethodUtils { @VisibleForTesting public static ArrayList getImplicitlyApplicableSubtypesLocked( Resources res, InputMethodInfo imi) { + final LocaleList systemLocales = res.getConfiguration().getLocales(); + + synchronized (sCacheLock) { + // We intentionally do not use InputMethodInfo#equals(InputMethodInfo) here because + // it does not check if subtypes are also identical. + if (systemLocales.equals(sCachedSystemLocales) && sCachedInputMethodInfo == imi) { + return new ArrayList<>(sCachedResult); + } + } + + // Note: Only resource info in "res" is used in getImplicitlyApplicableSubtypesLockedImpl(). + // TODO: Refactor getImplicitlyApplicableSubtypesLockedImpl() so that it can receive + // LocaleList rather than Resource. + final ArrayList result = + getImplicitlyApplicableSubtypesLockedImpl(res, imi); + synchronized (sCacheLock) { + // Both LocaleList and InputMethodInfo are immutable. No need to copy them here. + sCachedSystemLocales = systemLocales; + sCachedInputMethodInfo = imi; + sCachedResult = new ArrayList<>(result); + } + return result; + } + + private static ArrayList getImplicitlyApplicableSubtypesLockedImpl( + Resources res, InputMethodInfo imi) { final List subtypes = InputMethodUtils.getSubtypes(imi); final LocaleList systemLocales = res.getConfiguration().getLocales(); final String systemLocale = systemLocales.get(0).toString(); -- cgit v1.2.3-59-g8ed1b