[Android] [Memory Leak] InputMethodManager Memory leakage and Solution
Symptom:
InputMethodManager holds an Activity on the UI of the specified model k_touch_v9, causing the Activity to fail to be recycled. if the Activity is opened again, the old Activity will be released, but the newly opened Activity will be held and cannot be released and recycled. MAT shows Path to gc as follows:
Figure 1. Leak path
The version information of the phone number k_touch_v9 is as follows:
Figure 2. K_touch_v9
After some searches, someone has encountered this problem (see the reference link at the end of the article). The method is as follows:
@Overridepublic void onDestory() { //Fix memory leak: http://code.google.com/p/android/issues/detail?id=34731 InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.windowDismissed(this.getWindow().getDecorView().getWindowToken()); // hide method imm.startGettingWindowFocus(null); // hide method super.onDestory();}
However, after being used in practice, the Activity still exists, but path to gc points to unknown. For example:
Figure 3. Unknownpath
If the searched code is useless, try again.
To release the Activity, you just need to cut the path togc link. in this bug, there are two nodes mContext (DecorView) and mCurRootView (InputMethodManager) on the link for consideration. the following idea is to select a path to gc from the two nodes.
According to the source code, DecorView inherits from FrameLayout, and mContext is its context environment, which involves too much information and is not suitable for operations. the use of mCurRootView in InputMehtodManager is much simpler. After being initialized by a value assignment, the used scenario only has one judgment and one log printing. therefore, mCurRootView is selected as the breakthrough.To cut off its path to gc, set the mCurRootView to null using the Java Reflection method.(See the post-Article code).
After coding implementation, test again and find that there are still leaks, but the leaks have changed, such:
Figure 4. Leak path
The new leak point is mServedView/mNextServedView. You can use the same JavaReflection to leave it blank and cut the path to gc. but there is a problem here. If it is left empty, will it cause NullPointerException of InputMethodManager? Will it cause the system's internal logic crash? Check the source code again and find that mServedView and mNextServedView have always been empty-checking logic in the Code logic. Therefore, you can forcibly leave the blank to solve the problem.
Figure 5. Empty identification Logic
Finally, paste the code implementation:
Public static void fixInputMethodManagerLeak (Context context) {if (context = null) {return;} try {// empty mCurRootView mServedView mNextServedView... inputMethodManager imm = (InputMethodManager) context. getSystemService (Context. INPUT_METHOD_SERVICE); if (imm = null) {return;} // author: sodino mail: sodino@qq.com Object obj_get = null; Field f_mCurRootView = imm. getClass (). getDeclaredField ("MCurRootView"); Field f_mServedView = imm. getClass (). getDeclaredField ("mServedView"); Field f_mNextServedView = imm. getClass (). getDeclaredField ("mNextServedView"); if (values () = false) {f_mCurRootView.setAccessible (true);} obj_get = f_mCurRootView.get (imm); if (obj_get! = Null) {// if it is not null, it is set to null f_mCurRootView.set (imm, null);} if (f_mServedView.isAccessible () = false) {f_mServedView.setAccessible (true );} obj_get = f_mServedView.get (imm); if (obj_get! = Null) {// if it is not null, It is null f_mServedView.set (imm, null);} if (f_mNextServedView.isAccessible () = false) {f_mNextServedView.setAccessible (true );} obj_get = f_mNextServedView.get (imm); if (obj_get! = Null) {// if it is not null, set it to null f_mNextServedView.set (imm, null) ;}} catch (Throwable t) {t. printStackTrace ();}}
Execute the above method in the Activity. onDestory () method to solve the problem.
public void onDestroy() { super.ondestroy(); fixInputMethodManagerLeak(this); }
The problem seems to have been solved successfully, but is it true?
After the above processing, the memory leakage does not exist, but another problem occurs: There is an input box. After you click the input box, the input method interface cannot appear!
The procedure for recurrence at the scene of the accident is:
On the ActivityA page, click Activity B. There is an input box for Activity B. After clicking the input box, no input method is displayed. The reason is that the associated View of InputMethodManager has been left empty by the above Code.
The cause of the accident should start with the sequence of calling the life cycle method between activities:
The Calling sequence of the life cycle method from Activity A to Activity B is as follows:
A.onCreate()→A.onResume()→B.onCreate()→B.onResume()→A.onStop()→A.onDestroy()
That is to say, Activity B has been created and displayed. ActivityA executes onDestroy () to leave the associated View of InputMethodManager empty, and the input method cannot pop up.
If the cause is found, it is easy to solve the problem.
In the fixInputMethodManagerLeak (ContextdestContext) method, the Activity A to be destroyed is passed as the parameter. In the code, get the associated View of InputMethodManager and use View. getContext () is compared with Activity A. If the two are the same, they need to be recycled. If the two are different, A new interface is already using InputMethodManager, simply do not process it.
After modification, the final code is as follows:
Public static void fixInputMethodManagerLeak (Context destContext) {if (destContext = null) {return;} InputMethodManager imm = (InputMethodManager) destContext. getSystemService (Context. INPUT_METHOD_SERVICE); if (imm = null) {return;} String [] arr = new String [] {"mCurRootView", "mServedView", "mNextServedView "}; field f = null; Object obj_get = null; for (int I = 0; I <arr. length; I ++) {String param = Rr [I]; try {f = imm. getClass (). getDeclaredField (param); if (f. isAccessible () = false) {f. setAccessible (true);} // author: sodino mail: sodino@qq.comobj _ get = f. get (imm); if (obj_get! = Null & obj_get instanceof View) {View v_get = (View) obj_get; if (v_get.getContext () = destContext) {// The context referenced by InputMethodManager is the f. set (imm, null); // if it is left empty, the path to gc node is destroyed.} else {// does not want the target to be destroyed. That is, another layer of interface is entered, do not process, avoid affecting the original logic, and do not need to continue for loop if (QLog. isColorLevel () {QLog. d (ReflecterHelper. class. getSimpleName (), QLog. CLR, "fixInputMethodManagerLeak break, context is not suitable, get_context =" + v_get.getContext () + "dest_context =" + destContext);} break ;}} catch (Throwable t) {t. printStackTrace ();}}}
Reference:
L InputMethodManager: googlecode
L InputMethodManger-caused Activity Leakage
L MainActivity is not garbage collected after destruction because it is referenced byInputMethodManager indirectly
L InputMethodManagerholds reference to the tabhost-Memory Leak-OOM Error