天天看點

如何看待Handler使用中的警告如何看待Handler使用中的警告記憶體洩漏的可能性原因那麼如何解決這個問題呢?另一種方案總結

如何看待Handler使用中的警告

相信很多時候,我們都可以看到自己在使用Handler的過程中,好像得到了一個warning,雖然隻是一個警告,但這總是讓強迫症的我們有那麼一絲絲不爽。那麼應該如何解決這樣的警告呢?

如圖可見:

如何看待Handler使用中的警告如何看待Handler使用中的警告記憶體洩漏的可能性原因那麼如何解決這個問題呢?另一種方案總結

可以看到提示資訊:

This Handler class should be static or leaks might occur

[這個Handler類應該是靜态的,否則可能會發生記憶體洩漏]

對于我們嚴謹的開發者,應該使用Handler才是一種更好的體驗呢?

記憶體洩漏的可能性原因

為什麼這樣子使用的時候會可能産生記憶體洩漏呢?

因為這樣子的使用過程中,其實我們重寫了Handler的内部方法并生成了一個匿名内部類,而且由于建立匿名内部類或者内部類的執行個體會預設持有外部類對象的引用。

一種情況:當我們在Activity這樣子使用的時候,如果在執行一個耗時任務,當任務未執行完畢的時候,Activity被關閉了,Activity已不再使用,此時如果回收Activity對象,由于子線程未執行完畢,子線程持有Handler的引用,而Handler又持有Activity的引用,這樣導緻Activity對象無法被回收,即出現記憶體洩漏。

另一種情況:同樣是Activity異常關閉,這時候Handler消息傳遞機制中還存在未消耗的Message,也就是MessageQueue中還有Message沒有處理,這個時候也可能會發生記憶體洩漏,但是這個情況沒有更多的解釋。因為Handler是基于消息的。每次new 出Handler,都會建立一個消息隊列用于處理你使用handler發送的消息,形如:handler.send***Message。由于消息的發送總是會有先來後到的差別(如果隻是這樣都還好,畢竟再慢也不會太久,總歸可以跑完,可能會延遲個幾秒),但是如果你使用的是sendMessageDelayed(Message msg, long delayMillis)或postDelayed(Runnable r, long delayMillis)等發送延遲消息的時候,那基本記憶體洩漏發生的機率已經在90%以上了。

那麼如何解決這個問題呢?

有一位攻城獅RomainGuy(Android Framework)給出了一些建議。連結:

https://groups.google.com/forum/?fromgroups=#%21msg/android-developers/1aPZXZG6kWk/lIYDavGYn5UJ

如下為截圖部分:

如何看待Handler使用中的警告如何看待Handler使用中的警告記憶體洩漏的可能性原因那麼如何解決這個問題呢?另一種方案總結

另外,在androiddesignpatterns上也有人給出了解決方案:

http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html

解決方法主要在于兩點:

1.将Handler聲明為靜态内部類。因為靜态内部類不會持有外部類的引用,是以不會導緻外部類執行個體出現記憶體洩露。

2.在Handler中添加對外部Activity的弱引用。由于Handler被聲明為靜态内部類,不再持有外部類對象的引用,導緻無法在handleMessage()中操作Activity中的對象,是以需要在Handler中增加一個對Activity的弱引用。

改善之後的代碼如下:

private static class MyHandler extends Handler {  
        private final WeakReference<MainActivity> mActivity;  

        public MyHandler(MainActivity activity) {  
            this.mActivity = new WeakReference<MainActivity>(activity);  
        }  

        @Override  
        public void handleMessage(Message msg) {  
            MainActivity mainActivity = mActivity.get();  
            if (mainActivity == null) {  
                return;  
            }  
            // your code here  
        }  
    }  
           

WeakReference即弱引用,與強引用(即我們常說的“引用”)相對。它的特點是,GC在回收時會忽略掉弱引用,即就算有弱引用指向某對象,但隻要該對象沒有被強引用指向,該對象就會在被GC檢查到時回收掉。

改為采用WeakReference之後,如果任務未執行完但是使用者卻關閉了MainActivity,但由于僅有一個來自MyHandler的弱引用指向MainActivity,是以GC仍然會在檢查時把MainActivity回收掉。這樣,記憶體洩露的問題就不會出現了。

另一種方案

如果你網上一搜你會看到很多關于弱引用的文章。這确實是一個解決的辦法。其原理就是讓所有在handler裡面使用的對象都變成弱引用,目的就是為了可以在Android回收記憶體的時候,可以直接回收掉。我真覺得如果隻是寫這種辦法的人,絕對是屬于拷貝黨,因為這完全是就事論事。你想想就明白,我們寫這個Handler是因為我們要使用它。怎麼可以通過這種弱引用的辦法去處理這類問題呢?讓JVM想回收就回收?!如果這樣,那我們還需要在使用Bitmap的時候,recycle()幹嘛,還不如直接弄成軟引用得了。

如果你運氣好,你會碰到一些除了寫弱引用這個方法後,還有一個就是handler.removeCallbacksAndMessages(null);,就是移除所有的消息和回調,簡單一句話就是清空了消息隊列。注意,不要以為你post的是個Runnable或者隻是sendEmptyMessage。你可以看一下源碼,在handler裡面都是會把這些轉成正統的Message,放入消息隊列裡面,是以清空隊列就意味着這個Handler直接被打成原型了,當然也就可以回收了。

  是以,我覺得最好的辦法就是你在使用Handler的時候,在外面的Activity或者Fragment中的關閉方法中,如onDestroy中調用一下handler.removeCallbacksAndMessages(null);就可以了,不應該改成軟引用。

示例代碼:

Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return false;
        }
    })
           

最後在外部類的onDestory方法中插入如下代碼:

mHandler.removeCallbacksAndMessages(null);
           

總結

對比兩種方案,第一種方案需要一個靜态内部類,第二種方案隻需要重寫一個Callback接口,而且沒有可能出現記憶體洩漏的提示,至于那個較好,這個沒有實際的測試,看大家了!