常用代碼整理:Handler 的生產環境用法

說明:大部分內容都是參考別的文章,這裏做整理是爲了以後的編程有實用的模板,可以即需即用。

最簡單的 Handler 寫法:

private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 1:
                    break;
                default:
                    break;
            }
        }
    };

提醒說 “This Handler class should be static or leaks might occur”(這個處理程序類應該是靜態的,或者可能發生泄漏):

就算按照 IDE 的提示加上 “@SuppressLint(“HandlerLeak”)”,但實際上並沒有解決問題:

@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case 1:
                break;
            default:
                break;
        }
    }
};

Handler 造成的內存泄漏: Handler、Message 和 MessageQueue 都是相互關聯在一起的,如果 Handler 發送的 Message 尚未被處理,則該 Message 及發送它的 Handler 對象將被線程 MessageQueue 一直持有;由於 Handler 屬於 TLS(Thread Local Storage) 變量,生命週期和 Activity 是不一致的,很容易導致內存泄漏。

  • 線程局部存儲(TLS):用於將某些數據和一特定線程關聯起來,即,這些數據爲關聯線程所獨有(私有)。
  • TLS 是一個機制,經由它,程序可以擁有全域變量,但處於「每一線程各不相同」的狀態。也就是說,進程中的所有線程都可以擁有全域變量,但這些變量其實是特定對某個線程纔有意義。例如,你可能有一個多線程程序,每一個線程都對不同的文件寫文件(也因此它們使用不同的文件handle)。這種情況下,把每一個線程所使用的文件 handle 儲存在 TLS 中,將會十分方便。當線程需要知道所使用的 handle,它可以從TLS 獲得。重點在於:線程用來取得文件 handle 的那一段碼在任何情況下都是相同的,而從TLS 中取出的文件handle 卻各不相同。非常靈巧,不是嗎?有全域變數的便利,卻又分屬各線程。

解決這種問題的做法:使用 static 的 Handler內部類,同時在實現內部類中持有 Context 的弱引用。

爲什麼要使用靜態內部類:非靜態內部類默認持有外部類的引用,如果該非靜態內部類創建了一個靜態的實例,因爲該實例的生命週期和應用的生命週期一樣長,外部類的引用會一直被靜態實例持有,最終導致外部類的內存資源不能被正常回收。而避免這種情況的正確做法是,將該內部類設爲靜態內部類或將該內部類抽取出來封裝成一個單例。

避免習慣性地使用 static 變量:由於 static 變量會跟應用程序生命週期一致,當 Activity 退出後臺被後臺回收時,static 變量是不安全,所以也要管理好 static 變量的生命週期。

解決方案,抽取基類,方便編程【基本版】:

import android.os.Handler;
import java.lang.ref.WeakReference;

public class UIHandler<T> extends Handler {

    private WeakReference<T> ref;

    public UIHandler(T cls) {
        // 指示指定對象變得可終結,可被回收
        ref = new WeakReference<T>(cls);
    }

    public T getRef() {
        return ref != null ? ref.get() : null;
    }
}
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    private final MyHandler handler = new MyHandler(this);

    private static class MyHandler extends UIHandler<MainActivity> {

        public MyHandler(MainActivity cls) {
            super(cls);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            // 首先要判斷是否已被回收
            MainActivity activity = getRef();
            if (activity != null) {
                // 處理 Message
                switch (msg.what) {
                    case 1:
                        break;
                    default:
                        break;
                }
            }
        }
    }
}

本着我懶我最屌的原則,再抽取一下(不知道有沒有坑?)【最終版】:

import android.os.Handler;
import android.os.Message;
import java.lang.ref.WeakReference;

public abstract class UIHandler<T> extends Handler {

    private WeakReference<T> ref;

    public UIHandler(T cls) {
        // 指示指定對象變得可終結,可被回收
        ref = new WeakReference<T>(cls);
    }

    public T getRef() {
        return ref != null ? ref.get() : null;
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        // 首先要判斷是否已被回收
        T ref = getRef();
        if (ref != null) {
            // 處理 Message
            myHandleMessage(msg);
        }
    }

    public abstract void myHandleMessage(Message msg);
}
private final MyHandler handler = new MyHandler(this);

private static class MyHandler extends UIHandler<MainActivity> {

    public MyHandler(MainActivity cls) {
        super(cls);
    }

    @Override
    public void myHandleMessage(Message msg) {
        switch (msg.what) {
            case 1:
                break;
            default:
                break;
        }
    }
}

順便說一下,handleMessage(Message msg) 在源代碼中是空實現的,如果不需要 UIHandler 中 handleMessage(Message msg) 的操作,可以在 UIHandler 的子類中,重寫 handleMessage(Message msg),而不用 super.handleMessage(msg):


參考文章:
1、http://tool.oschina.net/uploads/apidocs/jdk-zh/java/lang/ref/WeakReference.html
2、https://blog.csdn.net/tanlongzhiwen1/article/details/41478801
3、https://blog.csdn.net/alcoholdi/article/details/54948058
4、http://www.xuebuyuan.com/210093.html
5、http://www.xuebuyuan.com/3206319.html
6、http://www.cppblog.com/Tim/archive/2012/07/04/181018.html
7、https://www.cnblogs.com/wxishang1991/p/4882775.html
8、https://www.imooc.com/article/25134?block_id=tuijian_wz
9、https://blog.csdn.net/u013356254/article/details/52463636
10、《裏面有一些表述是之前摘錄別的文章的,來源已不明,請見諒》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章