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
This commit is contained in:
@@ -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<InputMethodSubtype> sCachedResult;
|
||||
|
||||
private InputMethodUtils() {
|
||||
// This utility class is not publicly instantiable.
|
||||
}
|
||||
@@ -498,6 +510,32 @@ public class InputMethodUtils {
|
||||
@VisibleForTesting
|
||||
public static ArrayList<InputMethodSubtype> 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<InputMethodSubtype> 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<InputMethodSubtype> getImplicitlyApplicableSubtypesLockedImpl(
|
||||
Resources res, InputMethodInfo imi) {
|
||||
final List<InputMethodSubtype> subtypes = InputMethodUtils.getSubtypes(imi);
|
||||
final LocaleList systemLocales = res.getConfiguration().getLocales();
|
||||
final String systemLocale = systemLocales.get(0).toString();
|
||||
|
||||
Reference in New Issue
Block a user