Context內存泄漏問題

今天看了一篇介紹Context的文章,寫的不錯,裏面有一段內容平時開發時候沒有注意到,摘抄如下: 
在項目中,我們經常會遇到使用單例模式或者靜態static變量,雖然使用靜態類或者靜態變量很方便,但是也潛在很多的內存泄漏問題

6.1靜態資源導致的內存泄漏

你可能遇到以下這段代碼:

public class MyCustomResource {
    //靜態變量drawable
    private static Drawable drawable;

    public MyCustomResource(Context context) { //這個context對象就是Activity的Context
        Resources resources = context.getResources();
        drawable = resources.getDrawable(R.drawable.ic_launcher);
    }
}

請問,這段代碼有什麼問題?乍一看貌似沒啥問題,挺好的啊!其實不然,主要的問題在於靜態變量drawable這裏,我們知道靜態變量在整個應用的內存裏只保存一份,一旦創建就不會釋放該變量的內存,直到整個應用都銷燬纔會釋放static靜態變量的內存。可是以上drawable資源是由Context對象來獲得,從而靜態變量drawable會持有該Context對象的引用,也就意味着如果該Context對應的Activity退出finish掉的時候其實該Activity是不能完全釋放內存的,因爲靜態變量drawable持有該Activity的Context。從而導致該Activity內存無法回收,導致內存泄漏隱患。因爲Activity就是Context,所有Context的生命週期和Activity是一樣長的,我們希望Activity退出時Context也釋放內存,這樣纔不會導致內存泄漏隱患。那麼以上這段代碼是不安全的,如果代碼必須要使用靜態資源怎麼辦呢?其實我們可以這麼修改:

Context包括三種:Activity、Service和Application,一般而言Context對象都是Activity的context,在一篇我的這篇文章https://mp.csdn.net/postedit/80222796講到,一般就不要用Activity的context,容易出現內存泄漏的問題,儘量使用Application的Context。

public class MyCustomResource {
    //靜態變量drawable
    private static Drawable drawable;

    public MyCustomResource(Context context) {
        Resources resources = (context.getApplicationContext()).getResources();
        drawable = resources.getDrawable(R.drawable.ic_launcher);
    }

}

這裏我們修改成了使用getApplicationContext去獲取App的資源。由上一小節可知getApplicationContext返回的對象是Application的Context,而Application的生命週期和整個應用是一樣的,應用啓動Application被創建,整個應用退出Application銷燬。所以Application的Context的生命週期就是整個應用的生命週期,恰好可以用來獲取靜態資源。利用Application的Context就不會導致內存泄漏了。

6.2 單例模式導致內存泄漏

相信單例模式對開發者很有誘惑力吧!或多或少在項目中都有用過單例模式。你也可能見過一下這段代碼:

public class CustomManager {
    private static CustomManager sInstance;
    public static CustomManager getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new CustomManager(context);
        }
        return sInstance;
    }

    private Context mContext;
    private CustomManager(Context context) {
        mContext = context;
    }
}

同樣,以上代碼也存在內存泄漏的隱患。因爲單例模式使用的是靜態類的方式,讓該對象在整個應用的內存中保持一份該對象,從而減少對多次創建對象帶來的資源浪費。同樣的問題:在創建該單例的時候使用了生命週期端的Context對象的引用,如果你是在Application中創建以上單例的話是木有任何問題的。因爲Application的Context生命週期是整個應用,和單例的生命週期一樣,因此不會導致內存泄漏。但是,如果你是在Activity中創建以上單例的話,就會導致和6.1小節一樣的問題—內存泄漏。所以我們同樣可以將代碼修改成如下:

public class CustomManager {
    private static CustomManager sInstance;
    public static CustomManager getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new CustomManager(context.getApplicationContext());
        }
        return sInstance;
    }

    private Context mContext;
    private CustomManager(Context context) {
        mContext = context;
    }
}

6.3 總結

以後在使用Context對象獲取靜態資源,創建單例對象或者靜態方法的時候,請多考慮Context的生命週期,一定要記得不要使用Activity的Context,切記要使用生命週期長的Application的Context對象。但是並不是所有情況使用Application的Context對象,比如第4小節,在創建Dialog,View控件的時候都必須使用Activity的Context對象。

轉載自:http://blog.csdn.net/feiduclear_up CSDN 廢墟的樹

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