android 記憶體泄露小計,android小計
1 今天在調試android 程式時候,發現即使程式退出了,發現還佔用記憶體大概有15M.用MAT查看,經過多次GC操作,發現依舊是15.直覺告訴我,應該發生記憶體泄露了。然後利用MAT,查看Memory Leak。結果讓我很吃驚,發現是InputMethodManager。這個對象一直引用著Context。也就是Activity,導致它無法釋放記憶體。後來google 一下發現,
以下貼出解決辦法,希望給遇到類似情況的人,提供協助:
@Overrideprotected 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代碼時,發現原來它在採集裝置的位置資訊和應用程式的包名和相關簽名資訊,當然這也無可厚非,人家畢竟要驗證應用程式的包名和簽名)。這可咋辦,這個靜態又是私人的。對了,想到反射了。
以下貼出反射的代碼。
@Overridepublic 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 blocke.printStackTrace();}super.finish();}
然後再調試,發現這個問題總算解決了。
看來以後用第三方sdk的時候,得注意點了,不能隨便用啊。