天天看點

[Android Memory] App調試記憶體洩露之Context篇(上)

轉載自:http://www.cnblogs.com/qianxudetianxia/p/3645106.html

Context作為最基本的上下文,承載着Activity,Service等最基本元件。當有對象引用到Activity,并不能被回收釋放,必将造成大範圍的對象無法被回收釋放,進而造成記憶體洩漏。

下面針對一些常用場景逐一分析。

1. CallBack對象的引用

    先看一段代碼:

[Android Memory] App調試記憶體洩露之Context篇(上)
[Android Memory] App調試記憶體洩露之Context篇(上)

大家看看有什麼問題嗎?

    沒問題是吧,繼續看:

[Android Memory] App調試記憶體洩露之Context篇(上)
[Android Memory] App調試記憶體洩露之Context篇(上)

有問題嗎?

    哈哈,先Hold住一下,先來說一下android各版本釋出的曆史:

了解源碼的曆史,是很有益于我們分析android代碼的。

    好,開始分析代碼。

    首先,檢視setBackgroundDrawable(Drawable background)方法源碼裡面有一行代碼引起我們的注意:

    是以sBackground對view保持了一個引用,view對activity保持了一個引用。

    當退出目前Activity時,目前Activity本該釋放,但是因為sBackground是靜态變量,它的生命周期并沒有結束,而sBackground間接保持對Activity的引用,導緻目前Activity對象不能被釋放,進而導緻記憶體洩露。

    是以結論是:有記憶體洩露!

    到此結束了嗎?

    我發現網上太多直接抄或者間接抄這篇文章,一搜一大片,并且吸引了大量的Android初學者不斷的轉載學習。

    但是經過本人深入分析Drawable源碼,事情發生了一些變化。

    Android官方文檔的這篇文章是寫于2009年1月的,當時的Android Source至少是Froyo之前的。

    Froyo的Drawable的setCallback()方法的實作是這樣的:

在GingerBread的代碼還是如此的。

    但是當進入HoneyComb,也就是3.0之後的代碼我們發現Drawable的setCallback()方法的實作變成了:

也就是說3.0之後,Drawable使用了軟引用,把這個洩露的例子問題修複了。(至于軟引用怎麼解決了以後有機會再分析吧)

    是以最終結論是,在android3.0之前是有記憶體洩露,在3.0之後無記憶體洩露!

    如果認真比較代碼的話,Android3.0前後的代碼改進了大量類似代碼,前面的Cursor篇裡的例子也是在3.0之後修複了。

    從這個例子中,我們很好的發現了記憶體是怎麼通過回調洩露的,同時通過官方代碼的update也了解到了怎麼修複類似的記憶體洩露。

2. System Service對象

    通過各種系統服務,我們能夠做一些系統設計好的底層功能:

[Android Memory] App調試記憶體洩露之Context篇(上)
[Android Memory] App調試記憶體洩露之Context篇(上)

這些其實就是定義在Context裡的,按理說這些都是系統的服務,應該都沒問題,但是代碼到了各家廠商一改,事情發生了一些變化。

      一些廠商定義的服務,或者廠商自己修改了一些新的代碼導緻系統服務引用了Context對象不能及時釋放,我曾經碰到過Wifi,Storage服務都有記憶體洩露。

     我們改不了這些系統級應用,我們隻能修改自己的應用。

     解決方案就是:使用ApplicationContext代替Context。

     舉個例子吧:

3. Handler對象

    先看一段代碼:

Handler洩露的關鍵點有兩個:

    1). 内部類

    2). 生命周期和Activity不一定一緻

    第一點,Handler使用的比較多,經常需要在Activity中建立内部類,是以這種場景還是很多的。

    内部類持有外部類Activity的引用,當Handler對象有Message在排隊,則無法釋放,進而導緻Activity對象不能釋放。

    如果是聲明為static,則該内部類不持有外部Acitivity的引用,則不會阻塞Activity對象的釋放。

    如果聲明為static後,可在其内部聲明一個弱引用(WeakReference)引用外部類。

[Android Memory] App調試記憶體洩露之Context篇(上)
[Android Memory] App調試記憶體洩露之Context篇(上)

第二點,其實不單指内部類,而是所有Handler對象,如何解決上面說的Handler對象有Message在排隊,而不阻塞Activity對象釋放?

    解決方案也很簡單,在Activity onStop或者onDestroy的時候,取消掉該Handler對象的Message和Runnable。

    通過檢視Handler的API,它有幾個方法:removeCallbacks(Runnable r)和removeMessages(int what)等。

[Android Memory] App調試記憶體洩露之Context篇(上)
[Android Memory] App調試記憶體洩露之Context篇(上)

  上面的代碼太長?好吧,出大招:

有人會問,當Activity退出的時候,我還有好多事情要做,怎麼辦?我想一定有辦法的,比如用Service等等.

4. Thread對象

    同Handler對象可能造成記憶體洩露的原理一樣,Thread的生命周期不一定是和Activity生命周期一緻。

    而且因為Thread主要面向多任務,往往會造成大量的Thread執行個體。

    據此,Thread對象有2個需要注意的洩漏點:

    1). 建立過多的Thread對象

    2). Thread對象在Activity退出後依然在背景執行

    解決方案是:

    1). 使用ThreadPoolExecutor,在同時做很多異步事件的時候是很常用的,這個不細說。

    2). 當Activity退出的時候,退出Thread。

    第一點,例子太多,建議大家參考一下afinal中AsyncTask的實作學習。

    第二點,如何正常退出Thread,我在之前的博文中也提到過。示例代碼如下:

[Android Memory] App調試記憶體洩露之Context篇(上)
[Android Memory] App調試記憶體洩露之Context篇(上)

有人會問,當Activity退出的時候,我還有好多事情要做,怎麼辦?請看上面Handler的分析最後一行。

    (未完待續)

本文轉自demoblog部落格園部落格,原文連結http://www.cnblogs.com/0616--ataozhijia/p/3755894.html如需轉載請自行聯系原作者

demoblog

繼續閱讀