Get InputMethodManager in View only if needed
The perf regression found in my initial attempt [1] to instantiate
InputMethodManager (IMM) for each display revieled that when a Window
gained/lost focus,
getContext().getSystemService(InputMethodManager.class)
gets called for all the View objects that belong to the Window.
This CL introduces a private utility method
View.notifyFocusChangeToInputMethodManager()
to replace existing unnecessary acquisitions of IMM in View.java,
including the most concerning one View.onWindowFocusChanged().
There should be no negative side-effect in doing this optimization.
LatencyTests results:
testExpandNotificationsLatency on taimen-userdebug
without this CL:
results=[43, 46, 58, 47, 52, 59, 55, 59, 58, 46]
min: 43.0, max:59.0, avg:54.7, median:53.5, std_dev:5.967
with this CL:
results=[41, 58, 55, 59, 60, 67, 51, 55, 55, 55]
min: 41.0, max:67.0, avg:55.6, median:55.0, std_dev:6.344
[1]: I7242e765426353672823fcc8277f20ac361930d7
c53d78e992694e471ddaae73f9a30977db9cdb75
Bug: 115893206
Test: atest ActivityManagerMultiDisplayTests
Test: atest CtsInputMethodTestCases CtsInputMethodServiceHostTestCases
Test: atest FrameworksCoreTests:android.view.inputmethod.InputMethodManagerTest
Test: No perf regression observed in Bug 117434607
Change-Id: I5c64b23c3f5cb16f7f3fb9cdc2be063083566050
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c18187b..29d3742 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7315,17 +7315,16 @@
// Here we check whether we still need the default focus highlight, and switch it on/off.
switchDefaultFocusHighlight();
- InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
if (!gainFocus) {
if (isPressed()) {
setPressed(false);
}
- if (imm != null && mAttachInfo != null && mAttachInfo.mHasWindowFocus) {
- imm.focusOut(this);
+ if (mAttachInfo != null && mAttachInfo.mHasWindowFocus) {
+ notifyFocusChangeToInputMethodManager(false /* hasFocus */);
}
onFocusLost();
- } else if (imm != null && mAttachInfo != null && mAttachInfo.mHasWindowFocus) {
- imm.focusIn(this);
+ } else if (mAttachInfo != null && mAttachInfo.mHasWindowFocus) {
+ notifyFocusChangeToInputMethodManager(true /* hasFocus */);
}
invalidate(true);
@@ -7341,6 +7340,26 @@
notifyEnterOrExitForAutoFillIfNeeded(gainFocus);
}
+ /**
+ * Notify {@link InputMethodManager} about the focus change of the {@link View}.
+ *
+ * <p>Does nothing when {@link InputMethodManager} is not available.</p>
+ *
+ * @param hasFocus {@code true} when the {@link View} is being focused.
+ */
+ private void notifyFocusChangeToInputMethodManager(boolean hasFocus) {
+ final InputMethodManager imm =
+ getContext().getSystemService(InputMethodManager.class);
+ if (imm == null) {
+ return;
+ }
+ if (hasFocus) {
+ imm.focusIn(this);
+ } else {
+ imm.focusOut(this);
+ }
+ }
+
/** @hide */
public void notifyEnterOrExitForAutoFillIfNeeded(boolean enter) {
if (canNotifyAutofillEnterExitEvent()) {
@@ -12485,7 +12504,7 @@
mPrivateFlags3 &= ~PFLAG3_TEMPORARY_DETACH;
onFinishTemporaryDetach();
if (hasWindowFocus() && hasFocus()) {
- getContext().getSystemService(InputMethodManager.class).focusIn(this);
+ notifyFocusChangeToInputMethodManager(true /* hasFocus */);
}
notifyEnterOrExitForAutoFillIfNeeded(true);
}
@@ -12876,20 +12895,19 @@
* focus, false otherwise.
*/
public void onWindowFocusChanged(boolean hasWindowFocus) {
- InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
if (!hasWindowFocus) {
if (isPressed()) {
setPressed(false);
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
- if (imm != null && (mPrivateFlags & PFLAG_FOCUSED) != 0) {
- imm.focusOut(this);
+ if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
+ notifyFocusChangeToInputMethodManager(false /* hasFocus */);
}
removeLongPressCallback();
removeTapCallback();
onFocusLost();
- } else if (imm != null && (mPrivateFlags & PFLAG_FOCUSED) != 0) {
- imm.focusIn(this);
+ } else if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
+ notifyFocusChangeToInputMethodManager(true /* hasFocus */);
}
refreshDrawableState();
@@ -17981,10 +17999,7 @@
rebuildOutline();
if (isFocused()) {
- InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
- if (imm != null) {
- imm.focusIn(this);
- }
+ notifyFocusChangeToInputMethodManager(true /* hasFocus */);
}
}