前言
Android 中 Handler 是開發中特別常用的知識。本人將分兩篇來敘述 Handler。
- 第一篇———— Handler 的用法
- 第二篇———— 從源碼分析 Handler 消息機制的實現
Handler 的功能
Handler 最常用的功能就是更新 UI。因爲 Android 只能在主線程中更新 UI,但是更新 UI 之前往往要先更新數據,這就需要一些 I/O 操作,比如請求網絡數據,或者讀寫文件。但是這些 I/O 操作大部分都比較耗時。而 Android 中的耗時操作又需要在子線程中執行。所以當耗時操作完成後我們需要將更新 UI 的操作切換到主線程,或者說將更新的數據發送到主線程。此時就用到了 Handler。從上面的描述中我們可以看到 Handler 的作用是將數據從子線程發送到主線程裏去操作。那麼同樣的我們也可以將數據從主線程發送到子線程。此時,我可以得出結論,Handler 的功能是:實現線程間的數據傳遞。
Handler、Looper、Message 和 MessageQueue 的關係
Handler 向 MessageQueue 發送 Message,Looper 負責循環 MessageQueue 中的 Message 並向 Handler 分發 Message,最後 Handler 負責處理 Message。示意圖如下
Handler 用法
因爲 Handler 工作時需要 Looper,所以我們要爲當前線程創建一個 Looper。如下面代碼:
new Thread("Thread2"){
@Override public void run() {
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
}
}.start();
但是 Android 的主線程會爲我們默認初始化 Looper,所以如果在主線程中使用 Handler 就無需手動創建 Looper。通常情況下,我們使用 Handler 的默認工造函數創建 Handler 對象,並重寫其 handleMessage 方法。代碼如下:
private Handler handler = new Handler(){
@Override public void handleMessage(Message msg) {
//處理Message ,比如更新 UI
}
};
然後,在耗時操作完成後,發送攜帶數據的 Message,代碼如下:
new Thread("Thread3"){
@Override public void run() {
//耗時操作,比如請求網絡
Message msg = Message.obtain();
handler.sendMessage(msg);
}
}.start();
其次,Handler 還可以通過 post 方法將一個 Runnable 對象投遞到 Handler 中去處理。代碼如下:
handler.post(new Runnable() {
@Override public void run() {
}
});
在上述代碼中,我們使用匿名內部類的方式來使用 Handler,但是這種方式往往會導致內存泄露。因爲匿名內部類默認持有外部類的引用,如果我們在 Activity 中通過上述代碼使用 Handler,那麼上述 Handler 對象將持有 Activity 的引用。而執行耗時操作的子線程持有 Handler 的引用。如果我們在耗時操作完成之前退出 Activity,而耗時操作還沒有完成,那麼子線程將繼續在後臺執行,將導致 Activity 不能被正常的銷燬回收,所以將導致內存泄露。
Handler 引起的內存泄露的解決方法
使用靜態內部類
因爲靜態內部類不會持有外部類的引用,所以我們可以使用靜態內部類來解決內存泄露的問題,代碼如下:
static class MyHandler extends Handler{
WeakReference<Activity> activityWeakReference;
public MyHandler(Activity activity){
activityWeakReference = new WeakReference<Activity>(activity);
}
@Override public void handleMessage(Message msg) {
super.handleMessage(msg);
Activity activity = activityWeakReference.get();
if (activity!= null){
//更新 UI
}
}
}
完善程序邏輯
- 在關閉 Activity 的時候停掉你的後臺線程。線程停掉了,就相當於切斷了 Handler 和外部連接的線,Activity 自然會在合適的時候被回收。
- 如果 Handler 是被 delay 的 Message 持有了引用,那麼使用相應的 Handler 的 removeCallbacks() 方法,把消息對象從消息隊列移除就行了。