1 今天在调试android 程序时候,发现即使程序退出了,发现还占用内存大概有15M.用MAT查看,经过多次GC操作,发现依旧是15.直觉告诉我,应该发生内存泄露了。然后利用MAT,查看Memory Leak。结果让我很吃惊,发现是InputMethodManager。这个对象一直引用着Context。也就是Activity,导致它无法释放内存。后来google 一下发现,
以下贴出解决办法,希望给遇到类似情况的人,提供帮助:
@Override
protected void onDestroy()
{
super.onDestroy();
//fix for memory leak: http://code.google.com/p/android/issues/detail?id=34731
fixInputMethodManager();
}
private void fixInputMethodManager()
{
final Object imm = getSystemService(Context.INPUT_METHOD_SERVICE);
final Reflector.TypedObject windowToken
= new Reflector.TypedObject(getWindow().getDecorView().getWindowToken(), IBinder.class);
Reflector.invokeMethodExceptionSafe(imm, "windowDismissed", windowToken);
final Reflector.TypedObject view
= new Reflector.TypedObject(null, View.class);
Reflector.invokeMethodExceptionSafe(imm, "startGettingWindowFocus", view);
}
就是通过反射调用windowDismissed和startGettingWindowFocus。本来以为这个问题就这样搞定了,谁知道又出现了一个更加奇葩的情况。我发现竟然还有一个更奇葩的对象:com.lflytek.speech.a.a.a。脑子懵了一下,这是啥东东?通过百度这个查询,发现这是科大讯飞的语音识别sdk。对,脑子确实懵了。但是不对啊。这个对象怎么生命周期那么长。不对,肯定哪里搞错了。后来通过源代码阅读:
public RecognizerDialog(Context paramContext, String paramString)
{
super(paramContext);
this.a = new a(paramContext, paramString);
}
public void setListener(RecognizerDialogListener paramRecognizerDialogListener)
{
((a)this.a).a(paramRecognizerDialogListener);
}
这是sdk唯一能得到我应用程序的地方,其他的都是设置一些信息。难道问题就出在这个地方?对,你答对了!
表面上看起来这没有任何问题,你看我们很多应用程序sdk不都是这么弄的嘛,这很正常啊。没办法,虽然代码混淆过了,可我为了搞清楚事情真相只能硬着头皮去读。通过代码不断的追踪,终于发现秘密了。
在com.iflytek.speech.a.a中有一个很奇葩的私有的类成员。
private static a e=null
public static com.iflytek.speech.b b(Context paramContext, String paramString)
{
if (e == null)
e = new a(paramContext, paramString);
return e;
}
你这不是坑人吗?干嘛非要用静态呢,这样就造成了e的生命周期长与Activity,也就造成了内存泄露。(后来在看混淆过的科大讯飞sdk代码时,发现原来它在采集设备的位置信息和应用程序的包名和相关签名信息,当然这也无可厚非,人家毕竟要验证应用程序的包名和签名)。这可咋办,这个静态又是私有的。对了,想到反射了。
以下贴出反射的代码。
@Override
public void finish() {
com.iflytek.a.a.a=null;
try {
Class clazz=Class.forName("com.iflytek.speech.a.a");
Field field=clazz.getDeclaredField("e");
if (field.isAccessible()==false) {
field.setAccessible(true);
}
field.set(null, null);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
super.finish();
}
然后再调试,发现这个问题总算解决了。
看来以后用第三方sdk的时候,得注意点了,不能随便用啊。