[Android] Handler 詳解

前言

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 消息機制示意圖

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
        }
      }
    }

完善程序邏輯

  1. 在關閉 Activity 的時候停掉你的後臺線程。線程停掉了,就相當於切斷了 Handler 和外部連接的線,Activity 自然會在合適的時候被回收。
  2. 如果 Handler 是被 delay 的 Message 持有了引用,那麼使用相應的 Handler 的 removeCallbacks() 方法,把消息對象從消息隊列移除就行了。
發佈了29 篇原創文章 · 獲贊 2 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章