防止 Android 內存泄漏的 8 種方法


轉載於:http://android.jobbole.com/84810/


在上一篇 Android內存泄漏的八種可能(上)中,我們討論了八種容易發生內存泄漏的代碼。其中,尤其嚴重的是泄漏Activity對象,因爲它佔用了大量系統內存。不管內存泄漏的代碼表現形式如何,其核心問題在於:

在Activity生命週期之外仍持有其引用。

幸運的是,一旦泄漏發生且被定位到了,修復方法是相當簡單的。

Static Actitivities

這種泄漏

構造靜態變量持有Activity對象很容易造成內存泄漏,因爲靜態變量是全局存在的,所以當MainActivity生命週期結束時,引用仍被持有。這種寫法開發者是有理由來使用的,所以我們需要正確的釋放引用讓垃圾回收機制在它被銷燬的同時將其回收。

Android提供了特殊的Set類 https://developer.android.com/reference/java/lang/ref/package-summary.html#classes 允許開發者控制引用的“強度”。Activity對象泄漏是由於需要被銷燬時,仍然被強引用着,只要強引用存在就無法被回收。

可以用弱引用代替強引用。
https://developer.android.com/reference/java/lang/ref/WeakReference.html.

弱引用不會阻止對象的內存釋放,所以即使有弱引用的存在,該對象也可以被回收。

Static Views

靜態變量持有View

private static View view;

由於View持有其宿主Activity的引用,導致的問題與Activity一樣嚴重。弱引用是個有效的解決方法,然而還有另一種方法是在生命週期結束時清除引用,Activity#onDestory()方法就很適合把引用置空。

Inner Class

這種泄漏

於上述兩種情況相似,開發者必須注意用非靜態內部類,因爲靜態內部類持有外部類的隱式引用,容易導致意料之外的泄漏。然而內部類可以訪問外部類的私有變量,只要我們注意引用的生命週期,就可以避免意外的發生。

避免靜態變量

這樣持有內部類的成員變量是可以的。

Anonymous Classes

前面我們看到的都是持有全局生命週期的靜態成員變量引起的,直接或間接通過鏈式引用Activity導致的泄漏。這次我們用AsyncTask

Handler

Thread

全部都是因爲匿名類導致的。匿名類是特殊的內部類——寫法更爲簡潔。當需要一次性的特殊子類時,Java提供的語法糖能讓表達式最少化。這種很贊很偷懶的寫法容易導致泄漏。正如使用內部類一樣,只要不跨越生命週期,內部類是完全沒問題的。但是,這些類是用於產生後臺線程的,這些Java線程是全局的,而且持有創建者的引用(即匿名類的引用),而匿名類又持有外部類的引用。線程是可能長時間運行的,所以一直持有Activity的引用導致當銷燬時無法回收。
這次我們不能通過移除靜態成員變量解決,因爲線程是於應用生命週期相關的。爲了避免泄漏,我們必須捨棄簡潔偷懶的寫法,把子類聲明爲靜態內部類。

靜態內部類不持有外部類的引用,打破了鏈式引用。

所以對於AsyncTask

Handler

TimerTask

但是,如果你堅持使用匿名類,只要在生命週期結束時中斷線程就可以。

Sensor Manager

這種泄漏

使用Android系統服務不當容易導致泄漏,爲了Activity與服務交互,我們把Activity作爲監聽器,引用鏈在傳遞事件和回調中形成了。只要Activity維持註冊監聽狀態,引用就會一直持有,內存就不會被釋放。

在Activity結束時註銷監聽器

總結

Activity泄漏的案例我們已經都走過一遍了,其他都大同小異。建議日後遇到類似的情況時,就使用相應的解決方法。內存泄漏只要發生過一次,通過詳細的檢查,很容易解決並防範於未然。

是時候做最佳實踐者了!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章