Android中Handler內存泄漏分析及解決

Java使用有向圖機制榛斯,通過GC自動檢查內存中的對象(什么時候檢查由虛擬機決定)阶牍,如果GC發(fā)現一個或一組對象為不可到達狀態(tài)查描,則將該對象從內存中回收宵蛀。也就是說痴鳄,一個對象不被任何引用所指向瘟斜,則該對象會在被GC發(fā)現的時候被回收;另外痪寻,如果一組對象中只包含互相的引用螺句,而沒有來自它們外部的引用(例如有兩個對象A和B互相持有引用,但沒有任何外部對象持有指向A或B的引用)橡类,這仍然屬于不可到達蛇尚,同樣會被GC回收。

Java內存泄漏指的是進程中某些對象(垃圾對象)已經沒有使用價值了顾画,但是它們卻可以直接或間接地引用到導致無法被GC回收取劫。無用的對象占據著內存空間,使得實際可使用內存變小研侣,形象地說法就是內存泄漏了谱邪。

2.Android中使用Handler造成內存泄露原因分析

(1)Handler使用方法

Handler mHandler =newHandler() {@OverridepublicvoidhandleMessage(Message msg){super.handleMessage(msg);? ? }};

在使用handler時,這是一段很常見的代碼义辕。但是虾标,它卻會造成嚴重的內存泄漏問題。在實際編寫中灌砖,我們往往會得到如下警告:

This Handler class should bestaticorleaks mightoccur(anonymous android.os.Handler)

此處理程序類應為bestatic璧函,否則可能會發(fā)生泄漏(匿名android.os.Handler)

Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.

由于此處理程序被聲明為內部類,因此它可能會阻止外部類被垃圾收集基显。如果處理程序對主線程以外的線程使用循環(huán)器或MessageQueue蘸吓,則沒有問題。如果處理程序正在使用主線程的Looper或MessageQueue撩幽,則需要修復處理程序聲明库继,如下所示:將處理程序聲明為靜態(tài)類;在外部類中窜醉,實例化外部類的WeakReference宪萄,并在實例化處理程序時將此對象傳遞給處理程序;使用WeakReference對象對外部類的成員進行所有引用榨惰。

(2)內部類mHandler

簡單的內部類如下:

class OuterClass{

class InnerClass{}

}

以上代碼mHandler讓人并不覺得是內部類拜英,它并不像InnerClass那樣形象,但是其實以下句柄實現一個繼承Handler的類琅催,也就是自定義了一個類居凶,那么明顯它就是一個內部類虫给。其實它是屬于內部類一種:匿名內部類Anonymous Inner Class

{

@Override

public void handleMessage(Message msg){?

super.handleMessage(msg);? ??

?}

}

(3)Handler造成內存泄漏分析

當Android應用程序啟動時,Framework會為該應用程序的主線程創(chuàng)建一個Looper對象侠碧。這個Looper對象包含一個簡單的消息隊列Message Queue抹估,并且能夠循環(huán)的處理隊列中的消息。這些消息包括大多數應用程序Framework事件弄兜,例如Activity生命周期方法調用药蜻、點擊事件等,這些消息都會被添加到消息隊列中并被逐個處理挨队。另外谷暮,主線程的Looper對象會伴隨該應用程序的整個生命周期。

當在主線程中初始化Handler時盛垦,該Handler就會自動和主線程Looper的消息隊列關聯(lián)起來湿弦。所有發(fā)送到消息隊列的消息Message都會擁有一個對Handler的引用,所以當Looper來處理消息時腾夯,會據此回調Handler的handleMessage(Message)方法來分發(fā)處理該消息颊埃。

在Java里,非靜態(tài)內部類和匿名內部類都會潛在的引用它們所屬的外部類蝶俱。但是班利,靜態(tài)內部類不會引用外部類對象。

當使用內部類(包括匿名內部類)來創(chuàng)建Handler的時候榨呆,Handler對象會持有外部類對象(通常是一個Activity)的引用(不然怎么通過Handler來操作Activity中的View罗标?)。而Handler通常會伴隨著一個耗時的后臺線程(例如從網絡拉取圖片)一起出現积蜻,這個后臺線程在任務執(zhí)行完畢(例如圖片下載完畢)之后闯割,通過消息機制通知Handler,然后Handler把圖片更新到界面竿拆。然而宙拉,如果用戶在網絡請求過程中關閉了Activity,正常情況下丙笋,Activity不再被使用谢澈,它就有可能在GC檢查時被回收掉,但由于這時線程尚未執(zhí)行完御板,而該線程持有Handler的引用(不然它怎么發(fā)消息給Handler锥忿?),這個Handler又持有Activity的引用怠肋,就導致該Activity無法被回收(即內存泄露)缎谷,直到網絡請求結束(例如圖片下載完畢)。

如果執(zhí)行了Handler的postDelayed()方法,該方法會將Handler裝入一個Message列林,并把這條Message推到MessageQueue中,那么在你設定的delay到達之前酪惭,會有一條MessageQueue -> Message -> Handler -> Activity的鏈希痴,導致Activity被持有引用而無法被GC回收。

(4)內存泄漏的危害

虛擬機占用內存過高春感,導致OOM(內存溢出)砌创,程序出錯。對于Android應用來說鲫懒,就是用戶打開一個Activity嫩实,使用完之后關閉它,內存泄露窥岩;又打開甲献,又關閉,又泄露颂翼;幾次之后晃洒,程序占用內存超過系統(tǒng)限制。

3.Handler導致內存泄漏解決方法

方法一:通過程序邏輯進行保護

1.在關閉Activity的時候停掉后臺線程朦乏。線程停掉了球及,就相當于切斷了Handler和外部連接的線,Activity自然會在合適的時候被GC回收呻疹。

2.如果Handler是被delay的Message持有了引用吃引,那么使用Handler的removeCallbacks()方法,把消息對象從消息隊列移除就行了刽锤。

方法二:將Handler聲明為靜態(tài)類

靜態(tài)類不持有外部類的對象镊尺,所以Activity可以隨意被回收。代碼如下:

private static class MyHandler extends Handler{

@Override

public void handleMessage(Message msg){?

super.handleMessage(msg);? ?

??}

}

但其實沒這么簡單姑蓝。使用了以上代碼之后鹅心,你會發(fā)現,由于Handler不再持有外部類對象的引用纺荧,導致程序不允許你在Handler中操作Activity中的對象了旭愧。所以你需要在Handler中增加一個對Activity的弱引用(WeakReference):

private final MyHandler mHandler =new MyHandler(this);?

private static class MyHandler extends Handler{?

private final WeakReference mActivity;

public MyHandler(Activity activity){? ?

?? mActivity =newWeakReference(activity);? ? }

@Override

public void handleMessage(Message msg){

final Activity activity = mActivity.get();

if(activity !=null) {

// doSomething

}?

?? }?

?}

對于匿名類Runnable,同樣可以將其設置為靜態(tài)類:

private static final Runnable mRunnable =newRunnable() {

@Override

?public void run(){?

// doSomething

}??

};

4.什么是WeakReference宙暇?

WeakReference弱引用输枯,與強引用(即我們常說的引用)相對,它的特點是占贫,GC在回收時會忽略掉弱引用桃熄,即就算有弱引用指向某對象,但只要該對象沒有被強引用指向(實際上多數時候還要求沒有軟引用型奥,但此處軟引用的概念可以忽略)瞳收,該對象就會在被GC檢查到時回收掉碉京。對于上面的代碼,用戶在關閉Activity之后螟深,就算后臺線程還沒結束谐宙,但由于僅有一條來自Handler的弱引用指向Activity,所以GC仍然會在檢查的時候把Activity回收掉界弧。這樣凡蜻,內存泄露的問題就不會出現了。


鏈接:http://www.reibang.com/p/daffbb9ddc17

來源:簡書

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末垢箕,一起剝皮案震驚了整個濱河市划栓,隨后出現的幾起案子,更是在濱河造成了極大的恐慌条获,老刑警劉巖忠荞,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異月匣,居然都是意外死亡钻洒,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門锄开,熙熙樓的掌柜王于貴愁眉苦臉地迎上來素标,“玉大人,你說我怎么就攤上這事萍悴⊥吩猓” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵癣诱,是天一觀的道長计维。 經常有香客問我,道長撕予,這世上最難降的妖魔是什么鲫惶? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮实抡,結果婚禮上欠母,老公的妹妹穿的比我還像新娘。我一直安慰自己吆寨,他們只是感情好赏淌,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著啄清,像睡著了一般六水。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天掷贾,我揣著相機與錄音睛榄,去河邊找鬼。 笑死想帅,一個胖子當著我的面吹牛懈费,可吹牛的內容都是我干的。 我是一名探鬼主播博脑,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼票罐!你這毒婦竟也來了叉趣?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤该押,失蹤者是張志新(化名)和其女友劉穎疗杉,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體蚕礼,經...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡烟具,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了奠蹬。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朝聋。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖囤躁,靈堂內的尸體忽然破棺而出冀痕,到底是詐尸還是另有隱情,我是刑警寧澤狸演,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布言蛇,位于F島的核電站,受9級特大地震影響宵距,放射性物質發(fā)生泄漏腊尚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一满哪、第九天 我趴在偏房一處隱蔽的房頂上張望婿斥。 院中可真熱鬧,春花似錦翩瓜、人聲如沸受扳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽勘高。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間华望,已是汗流浹背蕊蝗。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留赖舟,地道東北人蓬戚。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像宾抓,于是被迫代替她去往敵國和親子漩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內容