【乾貨】避免Android中Context引發的內存泄露


視頻彙總首頁:http://edu.51cto.com/lecturer/index/user_id-4626073.htmlhtml


Context是咱們在編寫Android程序常常使用到的對象,意思爲上下文對象。 經常使用的有Activity的Context仍是有Application的Context。Activity用來展現活動界面,包含了不少的視圖,而視圖又含有圖片,文字等資源。在Android中內存泄露很容易出現,而持有不少對象內存佔用的Activity更加容易出現內存泄露,開發者須要特別注意這個問題。
設計模式


本文講介紹Android中Context,更具體的說是Activity內存泄露的狀況,以及如何避免Activity內存泄露,加速應用性能。微信


Drawable引發的內存泄露app


Drawable引發內存泄露這個問題是比較隱晦,難以察覺的。在閱讀了Romain Guy的Avoiding memory leaks,結合grepcode查看源碼才明白了。ide


在Android系統中,當咱們進行了屏幕旋轉,默認狀況下,會銷燬掉當前的Activity,並建立一個新的Activity並保持以前的狀態。在這個過程當中,Android系統會從新加載程序的UI視圖和資源。假設咱們有一個程序用到了一個很大的Bitmap圖像,咱們不想每次屏幕旋轉時都從新加載這個Bitmap對象,最簡單的辦法就是將這個Bitmap對象使用static修飾。性能


private static Drawable sBackground;
this


@Overridespa

protected void onCreate(Bundle state) {設計

super.onCreate(state);code


TextView label = new TextView(this);

label.setText("Leaks are bad");


if (sBackground == null) {

sBackground = getDrawable(R.drawable.large_bitmap);

}

label.setBackgroundDrawable(sBackground);


setContentView(label);

}


可是上面的方法在屏幕旋轉時有可能引發內存泄露,不管是咋一看仍是仔細看這段代碼,都很難發現哪裏引發了內存泄露。


當一個Drawable綁定到了View上,實際上這個View對象就會成爲這個Drawable的一個callback成員變量,上面的例子中靜態的sBackground持有TextView對象lable的引用,而lable只有Activity的引用,而Activity會持有其餘更多對象的引用。sBackground生命週期要長於Activity。當屏幕旋轉時,Activity沒法被銷燬,這樣就產生了內存泄露問題。


2.3.7及如下版本Drawable的setCallback方法的實現


public final void setCallback(Callback cb) {

mCallback = cb;

}


好在從4.0.1開始,引入了弱引用處理這個問題,弱引用在GC回收時,不會阻止GC回收其指向的對象,避免了內存泄露問題。


public final void setCallback(Callback cb) {

mCallback = new WeakReference<Callback>(cb);

}


單例引發的內存泄露


單例是咱們比較簡單經常使用的一種設計模式,然而若是單例使用不當也會致使內存泄露。 好比這樣一個例子,咱們使用餓漢式初始化單例,AppSettings咱們須要持有一個Context做爲成員變量,若是咱們按照下面的實現實際上是有問題。


public class AppSettings {    

private Context mAppContext;

private static AppSettings sInstance = new AppSettings();


//some other codes

public static AppSettings getInstance() {

return sInstance;

}


public final void setup(Context context) {

mAppContext = context;

}

}


sInstance做爲靜態對象,其生命週期要長於普通的對象,其中也包含Activity,當咱們進行屏幕旋轉,默認狀況下,系統會銷燬當前Activity,而後當前的Activity被一個單例持有,致使垃圾回收器沒法進行回收,進而產生了內存泄露。


解決的方法就是不持有Activity的引用,而是持有Application的Context引用。代碼以下修改


public final void setup(Context context) {

mAppContext = context.getApplicationContext();

}


條條方法返回Context


一般咱們想要獲取Context對象,主要有如下四種方法


  • View.getContext,返回當前View對象的Context對象,一般是當前正在展現的Activity對象。

  • Activity.getApplicationContext,獲取當前Activity所在的(應用)進程的Context對象,一般咱們使用Context對象時,要優先考慮這個全局的進程Context。

  • ContextWrapper.getBaseContext():用來獲取一個ContextWrapper進行裝飾以前的Context,能夠使用這個方法,這個方法在實際開發中使用並很少,也不建議使用。

  • Activity.this 返回當前的Activity實例,若是是UI控件須要使用Activity做爲Context對象,可是默認的Toast實際上使用ApplicationContext也能夠。


其餘內存泄露問題


  • Android中糟糕的AsyncTask

  • Android中Handler引發的內存泄露

  • Google爲什麼這樣設計OnSharedPreferenceChangeListener


避免內存泄露須謹記


  • 不要讓生命週期長於Activity的對象持有到Activity的引用

  • 儘可能使用Application的Context而不是Activity的Context

  • 儘可能不要在Activity中使用非靜態內部類,由於非靜態內部類會隱式持有外部類實例的引用(具體能夠查看細話Java:」失效」的private修飾符瞭解)。若是使用靜態內部類,將外部實例引用做爲弱引用持有。

  • 垃圾回收不能解決內存泄露,瞭解Android中垃圾回收機制

微信訂閱號

wKiom1XCvSHCrSJRAAC1mdY8jWo590.jpg

相關文章
相關標籤/搜索