4.InputMethodManager内存泄露现象及解决
<h3>[Android][Memory Leak] InputMethodManager内存泄露现象及解决</h3>
<p>现象:
在特定的机型天语k_touch_v9机型上,某个界面上出现InputMethodManager持有一Activity,导致该Activity无法回收.如果该Activity再次被打开,则旧的会释放掉,但新打开的会被继续持有无法释放回收。</p>
<pre><code class="language-java">@Override
public 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();
}
</code></pre>
<pre><code>这种方法没有真正解决泄漏问题。
要想让Activity释放掉,思路就是将path togc这个链路剪断就可以.在这个bug中这个链路上有两个节点mContext(DecorView)和 mCurRootView(InputMethodManager)可供考虑.下面思路就是从这两个节点中选择一个入手剪断path to gc即可.
阅读源码可知, DecorView继承自FrameLayout,mContext是其上下文环境,牵涉太多,不适合操作入手.mCurRootView在InputMehtodManager中的使用就简单得多了,在被赋值初始化后,被使用的场景只有一次判断及一次日志打印.所以这里选中mCurRootView为突破口.剪断其path to gc的操作为通过Java Reflection方法将mCurRootView置空即可(见文后代码).</code></pre>
<pre><code class="language-java">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[]{&quot;mCurRootView&quot;, &quot;mServedView&quot;, &quot;mNextServedView&quot;};
Field f = null;
Object obj_get = null;
for (int i = 0;i &lt; arr.length;i ++) {
String param = arr[i];
try{
f = imm.getClass().getDeclaredField(param);
if (f.isAccessible() == false) {
f.setAccessible(true);
} // author: sodino mail:sodino@qq.com
obj_get = f.get(imm);
if (obj_get != null &amp;&amp; obj_get instanceof View) {
View v_get = (View) obj_get;
if (v_get.getContext() == destContext) { // 被InputMethodManager持有引用的context是想要目标销毁的
f.set(imm, null); // 置空,破坏掉path to gc节点
} else {
// 不是想要目标销毁的,即为又进了另一层界面了,不要处理,避免影响原逻辑,也就不用继续for循环了
if (QLog.isColorLevel()) {
QLog.d(ReflecterHelper.class.getSimpleName(), QLog.CLR, &quot;fixInputMethodManagerLeak break, context is not suitable, get_context=&quot; + v_get.getContext()+&quot; dest_context=&quot; + destContext);
}
break;
}
}
}catch(Throwable t){
t.printStackTrace();
}
}
}
</code></pre>
<pre><code class="language-java">public void onDestroy() {
super.ondestroy();
fixInputMethodManagerLeak(this);
}
</code></pre>
<pre><code>这样才可以真正解决问题。</code></pre>