Android Handler機(jī)制之內(nèi)存泄漏

溢出啦啦.jpg

該文章屬于Android Handler系列文章遮精,如果想了解更多丽惭,請點擊
《Android Handler機(jī)制之總目錄》

前言

整個Handler機(jī)制系列文章到此就結(jié)束了陨享,相信大家基本已經(jīng)將整個Handler機(jī)制消化的差不多了鄙陡,現(xiàn)在就剩下最后一個知識點乃正,在平時開發(fā)中使用Handler有可能會導(dǎo)致內(nèi)存泄漏的問題诺苹。下面我們就一起去了解了解~~

內(nèi)存泄漏

內(nèi)存泄漏在官方的定義如下:

內(nèi)存泄漏(Memory Leak)是指程序中己動態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無法釋放咕晋,造成系統(tǒng)內(nèi)存的浪費,導(dǎo)致程序運行速度減慢甚至系統(tǒng)崩潰等嚴(yán)重后果筝尾。

那么翻譯成人話捡需,就是當(dāng)一個對象不再被使用時,本該被系統(tǒng)回收筹淫,但卻因為有另外一個正在使用的對象持有它的引用站辉,導(dǎo)致其不能被回收呢撞,造成內(nèi)存的浪費。

那么針對于Android系統(tǒng)來說饰剥,在Android系統(tǒng)中會為每個應(yīng)用程序分配相應(yīng)的內(nèi)存(根據(jù)手機(jī)廠商的不同殊霞,分配的內(nèi)存大小會有所差異)。也就是說對于每一個應(yīng)用程序來說其內(nèi)存是有限的汰蓉。那么當(dāng)某個應(yīng)用程序產(chǎn)生的內(nèi)存泄漏較多時绷蹲,導(dǎo)致達(dá)到應(yīng)用總的內(nèi)存閥值,那么就會導(dǎo)致應(yīng)用崩潰顾孽。

Handler內(nèi)存泄漏的情況討論

為了分析Handler內(nèi)存泄漏的具體情況祝钢,請參看以下示例代碼:

public class HandlerLeakageActivity extends BaseActivity {

    public static final int UPDATE_UI = 1;
    
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == UPDATE_UI) {
                updateUI();
            }
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_leakage);
        Message message = Message.obtain();
        message.what = UPDATE_UI;
        mHandler.sendMessageDelayed(message, 1000 * 3600 * 24);//發(fā)送延時24小時消息
    }
    
    //更新ui
    private void updateUI() {...}
 }

上述代碼邏輯很簡單,我們在HandlerLeakageActivity 中創(chuàng)建了內(nèi)部類Handler若厚,同時發(fā)送了一個延時為24小時的消息拦英。當(dāng)HandlerLeakageActivity 收到這個延遲消息后,那么接著會來更新UI测秸,同時我們可以得到以下引用鏈:

handler_refrence.png

其中的內(nèi)部類Handler 擁有當(dāng)前Activity的引用疤估,是因為在Java中,非靜態(tài)內(nèi)部類會持有外部類的引用霎冯,而Messagey擁有Handler的引用铃拇,是因為Message通過Looper的loop()方法取出后,需要相應(yīng)的Handler來處理消息(msg.target ==發(fā)送消息的Handler)沈撞。

那么在整個Handler機(jī)制下的引用關(guān)系如下圖所示:


handler_leakage.png

參照上圖慷荔,我們設(shè)想一種情況,假設(shè)我們在程序啟動的時候关串,首先進(jìn)入HandlerLeakageActivity 拧廊,然后又將其finish掉。那么就會出現(xiàn)晋修,因為延遲消息的遲遲不能被取出執(zhí)行,導(dǎo)致該Activity不能被系統(tǒng)回收倦春。從而造成上文我們提到過的內(nèi)存泄漏

那么問題來了睁本,什么時候引用鏈會斷開?

在文章《Android Handler機(jī)制之Message及Message回收機(jī)制 》
中忠怖,我們曾經(jīng)提到過呢堰,當(dāng)消息被Looper通過Loop()方法取出并執(zhí)行的時候凡泣,會執(zhí)行recycleUnchecked()方法來重置消息中的數(shù)據(jù)皮假,具體代碼如下:

void recycleUnchecked() {
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;//將關(guān)聯(lián)handler對象置為null
        callback = null;
        data = null;
        //省略部分代碼
    }

在該方法中將target =null,其中消息中的target為當(dāng)前發(fā)送該消息的Handler對象骂维。也就說只有消息被取出執(zhí)行后惹资,整個引用鏈才會斷開,那么相應(yīng)的Handler與使用該Handler的Activity才會被系統(tǒng)回收航闺。

解決方法

通過上文Handler內(nèi)存泄漏的問題分析潦刃,導(dǎo)致這種情況的發(fā)生的原因是內(nèi)部類Handler擁有當(dāng)前Activity的引用侮措。那么解決只要解決這個問題萝毛,我們就能處理Handler內(nèi)存泄漏啦滑黔。

使用靜態(tài)內(nèi)部類+弱引用的方式

public class HandlerLeakageActivity extends BaseActivity {
    public static final int UPDATE_UI = 1;
    
    private MyHandler mHandler = new MyHandler(this);
    //使用靜態(tài)內(nèi)部類
    private static class MyHandler extends Handler {

        private final WeakReference<HandlerLeakageActivity> mWeakReference;
        MyHandler(HandlerLeakageActivity activity) {
            mWeakReference = new WeakReference<HandlerLeakageActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            HandlerLeakageActivity activity = mWeakReference.get();
            if (activity != null) {
                activity.updateUI();
            }
        }
    }
        //更新ui
    private void updateUI() {...}
   }

為了保證不再持有當(dāng)前Activity的引用环揽,我們采用靜態(tài)內(nèi)部類的方式(靜態(tài)內(nèi)部類不會持有外部類引用),同時為了讓Handler在處理消息的時候歉胶,能夠調(diào)用外部類Activity的方法,所以我們這里采用弱引用的方式粥谬。

為什么要使用弱引用辫塌?

在Java中判斷一個對象到底是不是需要回收,都跟引用相關(guān)臼氨。在Java中引用分為了4類。

  • 強(qiáng)引用:只要引用存在感耙,垃圾回收器永遠(yuǎn)不會回收Object obj = new Object();而這樣 obj對象對后面new Object的一個強(qiáng)引用持隧,只有當(dāng)obj這個引用被釋放之后,對象才會被釋放掉只酥。
  • 軟引用:是用來描述,一些還有但并非必須的對象层皱,對于軟引用關(guān)聯(lián)著的對象,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前草冈,將會把這些對象列進(jìn)回收范圍之中進(jìn)行第二次回收瓮增。(SoftReference)
  • 弱引用:也是用來描述非必須的對象,但是它的強(qiáng)度要比軟引用更弱一些绷跑。被弱引用關(guān)聯(lián)的對象只能生存到下一次垃圾收集發(fā)生之前,當(dāng)垃圾收集器工作時谬运,無論當(dāng)前內(nèi)存是否足夠垦藏,都回回收掉被弱引用關(guān)聯(lián)的對象。(WeakReference)
  • 虛引用:也被稱為幽靈引用轰驳,它是最弱的一種關(guān)系弟灼。一個對象是否有引用的存在,完全不會對其生存時間構(gòu)成影響田绑,也無法通過一個虛引用來取得一個實例對象。

另創(chuàng)建一個類+弱引用的方式

如果你不想使用靜態(tài)內(nèi)部類+弱引用的方式俺陋,你也可以采用新建一個Handler類文件+弱引用的方式昙篙。這兩種代碼基本差不多,這里就不過多進(jìn)行介紹了苔可。

當(dāng)外部類生命周期結(jié)束時焚辅,清空消息

如果你不想采用上述的兩種方式苟鸯,還有一種方法就是在當(dāng)前Activity被finish掉的時候棚点,移除掉整個消息隊列中的所有消息。這樣就能保證Activity與Handler沒有直接的引用關(guān)系啦瘫析。

關(guān)于消息的刪除主要有三種方法,大家可以根據(jù)自己的項目需求來選擇相應(yīng)的方法咸包。具體如下所示:

(關(guān)于消息的刪除杖虾,如果有同學(xué)不是很熟悉,請參看《Android Handler機(jī)制之Message及Message回收機(jī)制》

void removeMessages(Handler h, int what, Object object)
void removeMessages(Handler h, Runnable r, Object object)
void removeCallbacksAndMessages(Handler h, Object object)
void removeCallbacksAndMessages(Object token) 

結(jié)合Activity的生命周期坟比,具體代碼如下所示:

 @Override
    protected void onDestroy() {
        super.onDestroy();
        //這里token傳null,會移除消息隊列中所有當(dāng)前Handler發(fā)送且未被執(zhí)行的消息
        mHandler.removeCallbacksAndMessages(null);

這里我使用removeCallbacksAndMessages(Object token) 方法來清空消息嚷往,需要注意的是如果token=null,該方法會移除消息隊列中所有當(dāng)前Handler發(fā)送且未被執(zhí)行的消息

總結(jié)

  • Handler在使用時,如果直接采用內(nèi)部類茄茁,有可能會導(dǎo)致內(nèi)存泄漏。
  • Handler內(nèi)存泄漏的主要原因是付燥,內(nèi)部類Handler擁有外部類Activity的引用愈犹,且不能保證消息的發(fā)送是否有較長延時。
  • 解決Handler內(nèi)存泄漏的主要方法有漩怎,采用靜態(tài)內(nèi)部類+弱引用,當(dāng)外部類生命周期結(jié)束時饭玲,清空消息等叁执。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市次哈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌琼牧,老刑警劉巖葛假,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異抱究,居然都是意外死亡带斑,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門妈候,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挂滓,“玉大人,你說我怎么就攤上這事赶站。” “怎么了想括?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵烙博,是天一觀的道長。 經(jīng)常有香客問我铺根,道長乔宿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任囤官,我火速辦了婚禮,結(jié)果婚禮上肝陪,老公的妹妹穿的比我還像新娘刑顺。我一直安慰自己,他們只是感情好蹲堂,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布柒竞。 她就那樣靜靜地躺著,像睡著了一般朽基。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上衅檀,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天霎俩,我揣著相機(jī)與錄音,去河邊找鬼杉适。 笑死学密,一個胖子當(dāng)著我的面吹牛传藏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播毯侦,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼侈离,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了卦碾?” 一聲冷哼從身側(cè)響起起宽,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤坯沪,失蹤者是張志新(化名)和其女友劉穎擒滑,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丐一,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡库车,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了潘拱。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拧略。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖禽最,靈堂內(nèi)的尸體忽然破棺而出袱饭,到底是詐尸還是另有隱情川无,我是刑警寧澤虑乖,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布疹味,位于F島的核電站,受9級特大地震影響糙捺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜坎缭,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坏快。 院中可真熱鬧哄尔,春花似錦、人聲如沸岭接。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窄锅。三九已至,卻和暖如春入偷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背殿雪。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工锋爪, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亏镰。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓拯爽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親逼肯。 傳聞我的和親對象是個殘疾皇子否副,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 內(nèi)存管理的目的 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題崎坊。簡單粗...
    晨光光閱讀 1,294評論 1 4
  • Android Handler機(jī)制系列文章整體內(nèi)容如下: Android Handler機(jī)制1之ThreadAnd...
    隔壁老李頭閱讀 8,188評論 8 57
  • 【Android Handler 消息機(jī)制】 前言 在Android開發(fā)中,我們都知道不能在主線程中執(zhí)行耗時的任務(wù)...
    Rtia閱讀 4,838評論 1 28
  • 懷疑是逼近真相的開始赋续。 《楚門的世界》這部電影闡述的就是這個道理另患。 電影男主角Truman是一個樂觀的挑戰(zhàn)者。很難...
    彼年_閱讀 1,104評論 0 1
  • 準(zhǔn)備了好久鸦列,昨天一早我們也加入了回家過年的大軍鹏倘!兒子心里滿滿的小雀躍,做什么事情都很興奮纤泵。 偷偷看了一...
    微愛hh閱讀 97評論 0 0