[Android][Memory Leak] InputMethodManager内存泄露现象及解决

现象: 在特定的机型天语k_touch_v9机型上,某个界面上出现InputMethodManager持有一Activity,导致该Activity无法回收.如果该Activity再次被打开,则旧的会释放掉,但新打开的会被继续持有无法释放回收.MAT显示Path to gc如下:

图1. Leak path

天语k_touch_v9手机版本信息:

图2. K_touch_v9

一番搜索后,已经有人也碰到过这个问题(见文章最后引用链接),给出的方法是:

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();

}

但在实践中使用后,没有真正解决,Activity仍存在,但path to gc指向为unknown.如下图:

图3. Unknownpath

搜索来的代码不管用,就再想办法.

要想让Activity释放掉,思路就是将path togc这个链路剪断就可以.在这个bug中这个链路上有两个节点mContext(DecorView)和 mCurRootView(InputMethodManager)可供考虑.下面思路就是从这两个节点中选择一个入手剪断path to gc即可. 阅读源码可知,

DecorView继承自FrameLayout,mContext是其上下文环境,牵涉太多,不适合操作入手.mCurRootView在InputMehtodManager中的使用就简单得多了,在被赋值初始化后,被使用的场景只有一次判断及一次日志打印.所以这里选中mCurRootView为突破口.剪断其path to gc的操作为通过Java Reflection方法将mCurRootView置空即可(见文后代码). 编码实现后,再测,发现仍有泄露,但泄露情况有所变化,如下图:

图4. Leak path

新的泄露点为mServedView/mNextServedView,可以通过同样的JavaReflection将其置空,剪断path to gc.但这里有个问题得小心,这里强制置空后,会不会引起InputMethodManager的NullPointerException呢?会不会引起系统内部逻辑崩溃?再次查阅源码,发现mServedView及mNextServedView在代码逻辑中一直有判空逻辑,所以这时就可以放心的强制置空来解决问题了.

图5. 判空逻辑

最后贴出代码实现:

public static void fixInputMethodManagerLeak(Context context) {

if (context == null) {

return;

}

try {

// 对 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 (f_mCurRootView.isAccessible() == false) {

f_mCurRootView.setAccessible(true);

}

obj_get = f_mCurRootView.get(imm);

if (obj_get != null) { // 不为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) { // 不为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) { // 不为null则置为空

f_mNextServedView.set(imm, null);

}

} catch (Throwable t) {

t.printStackTrace();

}

}

在Activity.onDestory()方法中执行以上方法即可解决.

public void onDestroy() {

super.ondestroy();

fixInputMethodManagerLeak(this);

}

事情看上去圆满的解决了,但真的是吗? 经过以上处理后,内存泄露是不存在了,但出现另外一个问题,就是有输入框的地方,点击输入框后,却无法出现输入法界面了! 事故现场复现的操作步骤为:

ActivityA界面,点击进入Activity B界面,B有输入框,点击输入框后,没有输入法弹出。原因是InputMethodManager的关联View已经被上面的那段代码置空了。 事故原因得从Activity间的生命周期方法调用顺序说起: 从Activity A进入Activity B的生命周期方法的调用顺序是:

A.onCreate()→A.onResume()→B.onCreate()→B.onResume()→A.onStop()→A.onDestroy()

也就是说,Activity B已经创建并显示了,ActivityA这里执行onDestroy()将InputMethodManager的关联View置空了,导致输入法无法弹出。 原因发现了,要解决也就简单了。 fixInputMethodManagerLeak(ContextdestContext)方法参数中将目标要销毁的Activity A作为参数传参进去。在代码中,去获取InputMethodManager的关联View,通过View.getContext()与Activity A进行对比,如果发现两者相同,就表示需要回收;如果两者不一样,则表示有新的界面已经在使用InputMethodManager了,直接不处理就可以了。 修改后,最终代码如下:

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 = 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 && 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, "fixInputMethodManagerLeak break, context is not suitable, get_context=" + v_get.getContext()+" dest_context=" + destContext);

}

break;

}

}

}catch(Throwable t){

t.printStackTrace();

}

}

}

Android InputMethodManager 导致的内存泄露及解决方案

内存分析工具 MAT 的使用

引用:

l InputMethodManager:googlecode

l InputMethodManger导致的Activity泄漏

l MainActivity is not garbage collected after destruction because it is referenced byInputMethodManager indirectly

l InputMethodManagerholds reference to the tabhost - Memory Leak - OOM Error

android inputmethodmanager内存泄露,InputMethodManager内存泄露现象及解决相关推荐

  1. Android之内存泄露、内存溢出、内存抖动分析

      内存 JAVA是在JVM所虚拟出的内存环境中运行的,内存分为三个区:堆.栈和方法区. 栈(stack):是简单的数据结构,程序运行时系统自动分配,使用完毕后自动释放.优点:速度快. 堆(heap) ...

  2. Android App定位和规避内存泄露方法研究

    from:http://site.douban.com/android/widget/notes/350758/note/167481484/ 工作中刚好用到,网上搜到的,觉得不错,与大家分享 And ...

  3. android oom工具,Android OOM-Heap,MAT工具检测内存泄露

    概述 在android的开发中,要时刻主要内存的分配和垃圾回收,因为系统为每一个dalvik虚拟机分配的内存是有限的,在google的G1中,分配的最大堆大小只有16M,后来的机器一般都为24M,实在 ...

  4. Android开发中常见的内存泄露案例以及解决方法总结

    Android开发中常见的内存泄露案例以及解决方法总结 参考文章: (1)Android开发中常见的内存泄露案例以及解决方法总结 (2)https://www.cnblogs.com/shen-hua ...

  5. Android 内存优化——常见内存泄露及优化方案

    如果一个无用对象(不需要再使用的对象)仍然被其他对象持有引用,造成该对象无法被系统回 收,以致该对象在堆中所占用的内存单元无法被释放而造成内存空间浪费,这中情况就是内存泄 露. 在 Android 开 ...

  6. leaks Android内存泄露,安卓-内存泄露总结

    一. 内存泄漏定义 内存泄漏指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是它们却可以直接或间接地引用到gc roots导致无法被GC回收.无用的对象占据着内存空间,使得实际可使用内存变小,形 ...

  7. 什么是内存溢出与内存泄露,几种常见导致内存泄露的写法

    最近朋友推荐了一篇关于内存溢出与内存泄漏的文章,感觉写的还不错,于是便在网上搜索了一番,对这块进行了加固,发现自己之前写的代码也存在一些内存泄漏的风险,所以弄懂内存泄漏与内存溢出是很有利于我们提高代码 ...

  8. 内存溢出、内存泄露和FULL GC

    内存溢出和内存泄露 内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory: 比如:内存中加载的数据量过于庞大,如一次从数据库取出过多数 ...

  9. java没错泄露_Java内存泄露问题

    内存泄露 很多人在谈论内存泄露问题,当然对于c/c++来说,这个应该是老掉牙的问题,但是很多Java人员也越来越多得讨论这个问题,我这里写个小结,希望对大家有一定的参考价值. 必须先要了解的 1.c/ ...

最新文章

  1. 入门Promise的正确姿势
  2. LAMP一体环境快速安装
  3. windows搭建gcc开发环境(msys2) objdump
  4. 再论CMMI和敏捷的对话
  5. 推荐几个电子/嵌入式方向的公众号
  6. java map清除值为null的元素_Java中的集合框架大总结
  7. C语言switch如何退出,C语言萌新,想问问如何让下面那个switch函数输出的结果继续...
  8. mvc4.0 @Styles.Render(转)
  9. java合同管理系统源码下载_合同管理系统 - 源码下载|行业应用软件|源代码 - 源码中国...
  10. 树形dp 没有上司的舞会
  11. Angr安装与使用之使用篇(九)
  12. 马哥Linux笔记整理
  13. 形容java工作者的句子_形容工作态度的句子
  14. Django项目nginx+gunicorn 部署
  15. 数字化运营管控是如何提升管理透明及效率的!
  16. 英语单词词性顺口溜_初小英语语法速记口诀大全——词类
  17. 小程序常见故障解决方法分享|微信小程序平台常见拒绝情形
  18. python人工智能方向怎么学_如何学习人工智能
  19. ThinkPHP3.0主入口配置,注册、登录案例
  20. Android -- XML属性

热门文章

  1. 运行jar包出现Exception in thread “main“ java.lang.ClassNotFoundException: edu.bigdata.mr.Demo02_WC
  2. java gif等比例缩放_调整动画GIF的大小,同时使用java保持动画
  3. 特斯拉Model 3 重要参数
  4. TypeScript Map 对象和TypeScript 元组
  5. 计算机游戏纸牌技巧,难怪老PC会有纸牌扫雷等经典游戏?终于知道真相了
  6. 为python3.6创建一个软链接
  7. Unity3D - 反射(Refrections)
  8. 【硬件配置教程】海康NVR硬盘录像机接入海康RTSP摄像头操作步骤
  9. 动规——【USACO3.3.5】A Game游戏 IOI'96
  10. c语言 求2到100的素数,2是素数吗(c语言输出100以内素数)