繼續,這張說說一些android中泄漏和溢出的細節點:
一、泄漏根源之Static
static是Java中的一個關鍵字,當用它來修飾成員變量時,那麼該變量就屬於該類,而不是該類的實例。所以用static修飾的變量,它的生命週期是很長的,如果用它來引用一些資源耗費過多的實例(Context的情況最多),這時就要謹慎處理。
public class Example {
private static Context mContext;
//省略
}
以上的代碼一般看來似乎沒什麼問題。但如果將Activity賦值到麼mContext的話。那麼即使該Activity已經onDestroy,但是由於仍有對象保存它的引用,因此該Activity依然不會被釋放。還沒完呢,再看看下邊的:
private static Drawable sBackground;
public void example (){
ImageView iv = new ImageView(this);
iv.setBackgroundDrawable(sBackground);
}
我發現有很多哥們都這麼寫,同樣看起來沒什麼問題,但我們來剖析一下:sBackground, 是一個靜態的變量,但是我們發現,我們並沒有顯式的保存Contex的引用,但是,因爲Imageview是View的子類,當Drawable與View連接之後,Drawable就將View設置爲一個回調:
public class View implements Drawable.Callback
/**
* The application environment this view lives in.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
protected Context mContext;
由於View中是包含Context的引用的,所以,實際上我們依然保存了Context的引用。所以最終的引用鏈如下:
Drawable->ImageView->Context
所以,最終該Context也沒有得到釋放,積累就發生了內存泄露。
如何纔能有效的避免這種引用的發生呢?
第一,應該儘量避免static成員變量引用資源耗費過多的實例,比如Context。
第二、Context儘量使用Application Context,因爲Application的Context的生命週期比較長(具體多次不作詳細解釋,各位去看文檔)。
第三、使用WeakReference代替強引用。比如可以使用WeakReference<Context> mContextRef(一位前輩提起的,具體不是很清楚,也在琢磨中)
二、泄漏根源之線程
線程也是造成內存泄露的一個重要的源頭。線程產生內存泄露的主要原因在於線程生命週期的不可控。先看段代碼。
new Thread(new Runnable() {
public void run() {
// TODO do something
}
}).start();
很平常的一段代碼是吧,也是咱經常這麼搞的。假設問題:假設run函數是一個很費時的操作(通常大數據的加載),在加載過程中(前提線程並沒有結束),將設備的豎屏變爲了橫屏,一般情況下當屏幕轉換時會重新創建Activity(當然你也可以設置不創建新的Activity,在此以創建新的爲論點),按照我們的想法,老的Activity應該會被銷燬纔對,然而事實上並非如此。
由於我們的線程是Activity的內部類,所以Thread中保存了Activity的一個引用,當Thread是不會被銷燬的直至run函數結束時,因此它所引用的老的Activity也不會被銷燬,因此就出現了內存泄露的問題。
有些人喜歡用Android提供的AsyncTask,但事實上AsyncTask的問題更加嚴重,Thread只有在run函數不結束時纔出現這種內存泄露問題,然而AsyncTask內部的實現機制是運用了ThreadPoolExcutor,該類產生的Thread對象的生命週期是不確定的,是應用程序無法控制的,因此如果AsyncTask作爲Activity的內部類,就更容易出現內存泄露的問題。
這種線程導致的內存泄露問題應該如何解決呢(引用前輩的話)?
第一、將線程的內部類,改爲靜態內部類。
第二、在線程內部採用弱引用保存Context引用。
public class ExampleAsyncTask<Params, Progress, Result, WeakTarget> extends
AsyncTask<Params, Progress, Result> {
@Override
protected final Result doInBackground(Params... params) {
//TODO: do something
return null;
}
}
小結:事實上,線程的問題並不僅僅在於內存泄露,還會帶來一些災難性的問題。例如:多線程併發安全(推薦下載張孝祥的多線程併發視頻,十分詳細)等等