From 4f0d290c5d112ebac434bd8de4635f7d42ea2df0 Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Sat, 25 Jun 2011 03:54:11 +0900 Subject: [PATCH] Avoid memory leak by by non-static Handler inner classes bug: 4901934 Change-Id: I870ab2e621ef3640a84468f09c074cdd726dc963 --- .../AccessibleInputMethodServiceProxy.java | 14 +++-- .../deprecated/voice/VoiceInput.java | 17 ++++-- .../inputmethod/keyboard/KeyboardView.java | 26 ++++---- .../inputmethod/latin/CandidateView.java | 14 +++-- .../android/inputmethod/latin/LatinIME.java | 60 +++++++++++-------- .../latin/StaticInnerHandlerWrapper.java | 42 +++++++++++++ 6 files changed, 122 insertions(+), 51 deletions(-) create mode 100644 java/src/com/android/inputmethod/latin/StaticInnerHandlerWrapper.java diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java index 043266c70..7199550a9 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleInputMethodServiceProxy.java @@ -18,7 +18,6 @@ package com.android.inputmethod.accessibility; import android.content.SharedPreferences; import android.inputmethodservice.InputMethodService; -import android.os.Handler; import android.os.Looper; import android.os.Message; import android.text.TextUtils; @@ -26,6 +25,7 @@ import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.StaticInnerHandlerWrapper; public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActionListener { private static final AccessibleInputMethodServiceProxy sInstance = @@ -42,18 +42,20 @@ public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActi private AccessibilityHandler mAccessibilityHandler; - private class AccessibilityHandler extends Handler { + private static class AccessibilityHandler + extends StaticInnerHandlerWrapper { private static final int MSG_NO_HOVER_SELECTION = 0; - public AccessibilityHandler(Looper looper) { - super(looper); + public AccessibilityHandler(AccessibleInputMethodServiceProxy outerInstance, + Looper looper) { + super(outerInstance, looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_NO_HOVER_SELECTION: - notifyNoHoverSelection(); + getOuterInstance().notifyNoHoverSelection(); break; } } @@ -82,7 +84,7 @@ public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActi private void initInternal(InputMethodService inputMethod, SharedPreferences prefs) { mInputMethod = inputMethod; - mAccessibilityHandler = new AccessibilityHandler(inputMethod.getMainLooper()); + mAccessibilityHandler = new AccessibilityHandler(this, inputMethod.getMainLooper()); } /** diff --git a/java/src/com/android/inputmethod/deprecated/voice/VoiceInput.java b/java/src/com/android/inputmethod/deprecated/voice/VoiceInput.java index b718ebbb7..8969a2168 100644 --- a/java/src/com/android/inputmethod/deprecated/voice/VoiceInput.java +++ b/java/src/com/android/inputmethod/deprecated/voice/VoiceInput.java @@ -19,6 +19,7 @@ package com.android.inputmethod.deprecated.voice; import com.android.inputmethod.latin.EditingUtils; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.StaticInnerHandlerWrapper; import android.content.ContentResolver; import android.content.Context; @@ -26,7 +27,6 @@ import android.content.Intent; import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; -import android.os.Handler; import android.os.Message; import android.os.Parcelable; import android.speech.RecognitionListener; @@ -132,13 +132,20 @@ public class VoiceInput implements OnClickListener { private final static int MSG_RESET = 1; - private final Handler mHandler = new Handler() { + private final UIHandler mHandler = new UIHandler(this); + + private static class UIHandler extends StaticInnerHandlerWrapper { + public UIHandler(VoiceInput outerInstance) { + super(outerInstance); + } + @Override public void handleMessage(Message msg) { if (msg.what == MSG_RESET) { - mState = DEFAULT; - mRecognitionView.finish(); - mUiListener.onCancelVoice(); + final VoiceInput voiceInput = getOuterInstance(); + voiceInput.mState = DEFAULT; + voiceInput.mRecognitionView.finish(); + voiceInput.mUiListener.onCancelVoice(); } } }; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 9dc019c61..6a60a9ddf 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -30,7 +30,6 @@ import android.graphics.Rect; import android.graphics.Region.Op; import android.graphics.Typeface; import android.graphics.drawable.Drawable; -import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.util.Log; @@ -53,6 +52,7 @@ import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; import com.android.inputmethod.keyboard.internal.SwipeTracker; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.StaticInnerHandlerWrapper; import java.util.ArrayList; import java.util.HashMap; @@ -193,9 +193,9 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { private static final String KEY_LABEL_REFERENCE_CHAR = "M"; private final int mKeyLabelHorizontalPadding; - private final UIHandler mHandler = new UIHandler(); + private final UIHandler mHandler = new UIHandler(this); - class UIHandler extends Handler { + public static class UIHandler extends StaticInnerHandlerWrapper { private static final int MSG_SHOW_KEY_PREVIEW = 1; private static final int MSG_DISMISS_KEY_PREVIEW = 2; private static final int MSG_REPEAT_KEY = 3; @@ -205,34 +205,40 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { private boolean mInKeyRepeat; + public UIHandler(KeyboardView outerInstance) { + super(outerInstance); + } + @Override public void handleMessage(Message msg) { + final KeyboardView keyboardView = getOuterInstance(); final PointerTracker tracker = (PointerTracker) msg.obj; switch (msg.what) { case MSG_SHOW_KEY_PREVIEW: - showKey(msg.arg1, tracker); + keyboardView.showKey(msg.arg1, tracker); break; case MSG_DISMISS_KEY_PREVIEW: - mPreviewText.setVisibility(View.INVISIBLE); + keyboardView.mPreviewText.setVisibility(View.INVISIBLE); break; case MSG_REPEAT_KEY: tracker.onRepeatKey(msg.arg1); - startKeyRepeatTimer(mKeyRepeatInterval, msg.arg1, tracker); + startKeyRepeatTimer(keyboardView.mKeyRepeatInterval, msg.arg1, tracker); break; case MSG_LONGPRESS_KEY: - openMiniKeyboardIfRequired(msg.arg1, tracker); + keyboardView.openMiniKeyboardIfRequired(msg.arg1, tracker); break; case MSG_LONGPRESS_SHIFT_KEY: - onLongPressShiftKey(tracker); + keyboardView.onLongPressShiftKey(tracker); break; } } public void showKeyPreview(long delay, int keyIndex, PointerTracker tracker) { + final KeyboardView keyboardView = getOuterInstance(); removeMessages(MSG_SHOW_KEY_PREVIEW); - if (mPreviewText.getVisibility() == VISIBLE || delay == 0) { + if (keyboardView.mPreviewText.getVisibility() == VISIBLE || delay == 0) { // Show right away, if it's already visible and finger is moving around - showKey(keyIndex, tracker); + keyboardView.showKey(keyIndex, tracker); } else { sendMessageDelayed( obtainMessage(MSG_SHOW_KEY_PREVIEW, keyIndex, 0, tracker), delay); diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java index 09fd3b473..bb1ccbfcd 100644 --- a/java/src/com/android/inputmethod/latin/CandidateView.java +++ b/java/src/com/android/inputmethod/latin/CandidateView.java @@ -21,7 +21,6 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Typeface; -import android.os.Handler; import android.os.Message; import android.text.Spannable; import android.text.SpannableString; @@ -95,23 +94,28 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo private boolean mShowingAutoCorrectionInverted; private boolean mShowingAddToDictionary; - private final UiHandler mHandler = new UiHandler(); + private final UiHandler mHandler = new UiHandler(this); - private class UiHandler extends Handler { + private static class UiHandler extends StaticInnerHandlerWrapper { private static final int MSG_HIDE_PREVIEW = 0; private static final int MSG_UPDATE_SUGGESTION = 1; private static final long DELAY_HIDE_PREVIEW = 1000; private static final long DELAY_UPDATE_SUGGESTION = 300; + public UiHandler(CandidateView outerInstance) { + super(outerInstance); + } + @Override public void dispatchMessage(Message msg) { + final CandidateView candidateView = getOuterInstance(); switch (msg.what) { case MSG_HIDE_PREVIEW: - hidePreview(); + candidateView.hidePreview(); break; case MSG_UPDATE_SUGGESTION: - updateSuggestions(); + candidateView.updateSuggestions(); break; } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 9c6465dd2..52935ef7d 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -29,7 +29,6 @@ import android.inputmethodservice.InputMethodService; import android.media.AudioManager; import android.net.ConnectivityManager; import android.os.Debug; -import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.SystemClock; @@ -206,9 +205,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private CharSequence mEnteredText; - public final UIHandler mHandler = new UIHandler(); + public final UIHandler mHandler = new UIHandler(this); - public class UIHandler extends Handler { + public static class UIHandler extends StaticInnerHandlerWrapper { private static final int MSG_UPDATE_SUGGESTIONS = 0; private static final int MSG_UPDATE_OLD_SUGGESTIONS = 1; private static final int MSG_UPDATE_SHIFT_STATE = 2; @@ -218,42 +217,50 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private static final int MSG_SPACE_TYPED = 6; private static final int MSG_SET_BIGRAM_PREDICTIONS = 7; + public UIHandler(LatinIME outerInstance) { + super(outerInstance); + } + @Override public void handleMessage(Message msg) { - final KeyboardSwitcher switcher = mKeyboardSwitcher; + final LatinIME latinIme = getOuterInstance(); + final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher; final LatinKeyboardView inputView = switcher.getKeyboardView(); switch (msg.what) { case MSG_UPDATE_SUGGESTIONS: - updateSuggestions(); + latinIme.updateSuggestions(); break; case MSG_UPDATE_OLD_SUGGESTIONS: - mRecorrection.fetchAndDisplayRecorrectionSuggestions(mVoiceProxy, mCandidateView, - mSuggest, mKeyboardSwitcher, mWord, mHasUncommittedTypedChars, - mLastSelectionStart, mLastSelectionEnd, mSettingsValues.mWordSeparators); + latinIme.mRecorrection.fetchAndDisplayRecorrectionSuggestions( + latinIme.mVoiceProxy, latinIme.mCandidateView, + latinIme.mSuggest, latinIme.mKeyboardSwitcher, latinIme.mWord, + latinIme.mHasUncommittedTypedChars, latinIme.mLastSelectionStart, + latinIme.mLastSelectionEnd, latinIme.mSettingsValues.mWordSeparators); break; case MSG_UPDATE_SHIFT_STATE: switcher.updateShiftState(); break; case MSG_SET_BIGRAM_PREDICTIONS: - updateBigramPredictions(); + latinIme.updateBigramPredictions(); break; case MSG_VOICE_RESULTS: - mVoiceProxy.handleVoiceResults(preferCapitalization() + latinIme.mVoiceProxy.handleVoiceResults(latinIme.preferCapitalization() || (switcher.isAlphabetMode() && switcher.isShiftedOrShiftLocked())); break; case MSG_FADEOUT_LANGUAGE_ON_SPACEBAR: if (inputView != null) { inputView.setSpacebarTextFadeFactor( - (1.0f + mSettingsValues.mFinalFadeoutFactorOfLanguageOnSpacebar) / 2, + (1.0f + latinIme.mSettingsValues. + mFinalFadeoutFactorOfLanguageOnSpacebar) / 2, (LatinKeyboard)msg.obj); } sendMessageDelayed(obtainMessage(MSG_DISMISS_LANGUAGE_ON_SPACEBAR, msg.obj), - mSettingsValues.mDurationOfFadeoutLanguageOnSpacebar); + latinIme.mSettingsValues.mDurationOfFadeoutLanguageOnSpacebar); break; case MSG_DISMISS_LANGUAGE_ON_SPACEBAR: if (inputView != null) { inputView.setSpacebarTextFadeFactor( - mSettingsValues.mFinalFadeoutFactorOfLanguageOnSpacebar, + latinIme.mSettingsValues.mFinalFadeoutFactorOfLanguageOnSpacebar, (LatinKeyboard)msg.obj); } break; @@ -263,7 +270,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar public void postUpdateSuggestions() { removeMessages(MSG_UPDATE_SUGGESTIONS); sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTIONS), - mSettingsValues.mDelayUpdateSuggestions); + getOuterInstance().mSettingsValues.mDelayUpdateSuggestions); } public void cancelUpdateSuggestions() { @@ -277,7 +284,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar public void postUpdateOldSuggestions() { removeMessages(MSG_UPDATE_OLD_SUGGESTIONS); sendMessageDelayed(obtainMessage(MSG_UPDATE_OLD_SUGGESTIONS), - mSettingsValues.mDelayUpdateOldSuggestions); + getOuterInstance().mSettingsValues.mDelayUpdateOldSuggestions); } public void cancelUpdateOldSuggestions() { @@ -287,7 +294,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar public void postUpdateShiftKeyState() { removeMessages(MSG_UPDATE_SHIFT_STATE); sendMessageDelayed(obtainMessage(MSG_UPDATE_SHIFT_STATE), - mSettingsValues.mDelayUpdateShiftState); + getOuterInstance().mSettingsValues.mDelayUpdateShiftState); } public void cancelUpdateShiftState() { @@ -297,7 +304,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar public void postUpdateBigramPredictions() { removeMessages(MSG_SET_BIGRAM_PREDICTIONS); sendMessageDelayed(obtainMessage(MSG_SET_BIGRAM_PREDICTIONS), - mSettingsValues.mDelayUpdateSuggestions); + getOuterInstance().mSettingsValues.mDelayUpdateSuggestions); } public void cancelUpdateBigramPredictions() { @@ -309,23 +316,26 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } public void startDisplayLanguageOnSpacebar(boolean localeChanged) { + final LatinIME latinIme = getOuterInstance(); removeMessages(MSG_FADEOUT_LANGUAGE_ON_SPACEBAR); removeMessages(MSG_DISMISS_LANGUAGE_ON_SPACEBAR); - final LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); + final LatinKeyboardView inputView = latinIme.mKeyboardSwitcher.getKeyboardView(); if (inputView != null) { - final LatinKeyboard keyboard = mKeyboardSwitcher.getLatinKeyboard(); + final LatinKeyboard keyboard = latinIme.mKeyboardSwitcher.getLatinKeyboard(); // The language is always displayed when the delay is negative. final boolean needsToDisplayLanguage = localeChanged - || mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar < 0; + || latinIme.mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar < 0; // The language is never displayed when the delay is zero. - if (mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar != 0) { + if (latinIme.mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar != 0) { inputView.setSpacebarTextFadeFactor(needsToDisplayLanguage ? 1.0f - : mSettingsValues.mFinalFadeoutFactorOfLanguageOnSpacebar, keyboard); + : latinIme.mSettingsValues.mFinalFadeoutFactorOfLanguageOnSpacebar, + keyboard); } // The fadeout animation will start when the delay is positive. - if (localeChanged && mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar > 0) { + if (localeChanged + && latinIme.mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar > 0) { sendMessageDelayed(obtainMessage(MSG_FADEOUT_LANGUAGE_ON_SPACEBAR, keyboard), - mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar); + latinIme.mSettingsValues.mDelayBeforeFadeoutLanguageOnSpacebar); } } } @@ -333,7 +343,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar public void startDoubleSpacesTimer() { removeMessages(MSG_SPACE_TYPED); sendMessageDelayed(obtainMessage(MSG_SPACE_TYPED), - mSettingsValues.mDoubleSpacesTurnIntoPeriodTimeout); + getOuterInstance().mSettingsValues.mDoubleSpacesTurnIntoPeriodTimeout); } public void cancelDoubleSpacesTimer() { diff --git a/java/src/com/android/inputmethod/latin/StaticInnerHandlerWrapper.java b/java/src/com/android/inputmethod/latin/StaticInnerHandlerWrapper.java new file mode 100644 index 000000000..89d9ea844 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/StaticInnerHandlerWrapper.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +import android.os.Handler; +import android.os.Looper; + +import java.lang.ref.WeakReference; + +public class StaticInnerHandlerWrapper extends Handler { + final private WeakReference mOuterInstanceRef; + + public StaticInnerHandlerWrapper(T outerInstance) { + super(); + if (outerInstance == null) throw new NullPointerException("outerInstance is null"); + mOuterInstanceRef = new WeakReference(outerInstance); + } + + public StaticInnerHandlerWrapper(T outerInstance, Looper looper) { + super(looper); + if (outerInstance == null) throw new NullPointerException("outerInstance is null"); + mOuterInstanceRef = new WeakReference(outerInstance); + } + + public T getOuterInstance() { + return mOuterInstanceRef.get(); + } +}