Android Handler 消息機制詳述

版權(quán)聲明: 本文來自 書生依舊 的簡書普气,轉(zhuǎn)載請注明出處煌张。
原文鏈接: http://www.reibang.com/p/1a5a3db45cfa

一. What换可、Handler 是什么

Handler 與 Message跨算、MessageQueue右钾、Looper 一起構(gòu)成了 Android 的消息機制轻要,Android 系統(tǒng)通過大量的消息來與用戶進行交互抹凳,View 的繪制、點擊事件伦腐、Activity 的生命周期回調(diào)等都作為消息由主線程的 Handler 來處理赢底。

Handler 在消息機制中的作用是:發(fā)送和處理消息。

Handler 還有另一個重要的作用柏蘑,跨線程通信幸冻。最常見的就是子線程請求網(wǎng)絡(luò),然后使用 Handler 將請求到的數(shù)據(jù) post 到主線程刷新 UI咳焚,大名鼎鼎的 Retrofit 也是這么做的洽损。

二. How、如何使用 Handler

  • 創(chuàng)建 Handler

    private Handler handler = new Handler() {
      // 重寫 handleMessage 來根據(jù)不同 what 來處理 Message
      // 這個方法在 Handler 創(chuàng)建的線程執(zhí)行
      @Override public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 0:
                  MLog.i(msg.obj);
                  break;
                case 1:
                  break;
                default:
    
            }
        }
    };
    
  • 創(chuàng)建并發(fā)送 Message

    // 獲取一個 Message
    Message message = Message.obtain();
    message.what = 0;
    message.obj = new Object();
    // 使用 Handler 發(fā)送 Message
    // 消息發(fā)送完成后 Handler 的 handleMessage(Message msg) 會處理消息
    handler.sendMessage(message);
    
    // 延遲 1s 發(fā)送 Message
    handler.sendMessageDelayed(message, 1000);
    // 發(fā)送一個空的 Message
    handler.sendEmptyMessage(msg.what);  
    // 延遲發(fā)送一個空的 Message
    handler.sendEmptyMessageDelayed(0, 1000);
    
    // 還可以這樣
    // 創(chuàng)建 Message 并綁定 Handler
    Message message = handler.obtainMessage();
    message.what = 0;
    message.obj = new Object();
    
    // 發(fā)送 Message
    message.sendToTarget();
    
  • 使用 Handler 子線程請求數(shù)據(jù)革半,主線程刷新 UI

    // 1. 在主線程創(chuàng)建 Handler(略)
    // 2. 子線程請求數(shù)據(jù)碑定,主線程刷新 UI
    new Thread(new Runnable() {
        @Override public void run() {
            // 獲取網(wǎng)絡(luò)數(shù)據(jù)
            final List<Object> datas = getNetData();
    
            // 方法一:將數(shù)據(jù)作為 Message 的 obj 發(fā)送出去,在 handleMessage 中刷新 UI
            Message msg = Message.obtain();
            msg.what = 1;
            msg.obj = data;
            handler.sendMessage(msg);
    
            // 方法二:直接在 post 中刷新 UI
            handler.post(new Runnable() {
                @Override public void run() {
                  // 使用 datas 刷新 UI
                  // 這個方法也會在 Handler 創(chuàng)建的線程執(zhí)行
                }
            });
        }
    }).start();
    

三. Handler 的內(nèi)存泄漏

不得不說又官,上面使用 Handler 的方法會有內(nèi)存泄漏的風險

  • Handler 內(nèi)存泄漏的兩個原因

    • Java 中非靜態(tài)內(nèi)部類和匿名內(nèi)部類會持有外部類的引用

      // 這是一個外部類 Handler 不會持有外部類引用
      // 顯然 handleMessage 沒地方寫了
      Handler handler = new Handler();
      
      // 重寫 handleMessage 后將得到一個內(nèi)部類 Handler延刘,以內(nèi) handleMessage 是在外部類中實現(xiàn)的
      // 它持有外部類引用,可能會引起內(nèi)存泄漏
      Handler handler = new Handler() {
        @Override public void handleMessage(Message msg) {
              super.handleMessage(msg);
              switch (msg.what) {
                  case 0:
                    MLog.i(msg.obj);
                    break;
                  case 1:
                    break;
                  default:
      
              }
          }
      };
      
      // 這里 Handler 是一個匿名類六敬,但不是內(nèi)部類
      // Runnable 是一個匿名內(nèi)部類碘赖,持有外部類引用,可能會引起內(nèi)存泄漏
      new Handler().post(new Runnable() {
          @Override public void run() {
            // ...
          }
      });
      

      ?

    • Handler 的生命周期比外部類長外构。

  • 分析

    • 非靜態(tài)的內(nèi)部 Handler 子類普泡、匿名 Handler 子類會持有外部類的引用(Activity),而 Handler 可能會因為要等待處理耗時操作導致存活時間超過 Activity审编,或者消息隊列中存在未被 Looper 處理的 Message 撼班,而 Message 會持有 Handler 的引用。于是垒酬,在 Activity 退出時砰嘁,其引用還是被 Handler 持有眯亦,導致 Activity 無法被及時回收,造成內(nèi)存泄露般码。
    • 非靜態(tài)的內(nèi)部 Runnable 子類妻率、匿名 Runnable 子類 post 到任意 Handler 上時,Runnable 其實是 Massage中的 Callback板祝,持有 Message 引用宫静,如果這個 Massage 在消息隊列還沒有被處理,那么就會造成 Runnable 一直持有外部類的引用而造成內(nèi)存泄露券时。
  • 解決方案:

    • 通過靜態(tài)內(nèi)部類或者外部類來聲明 Handler 和 Runnable孤里。
    • 通過弱引用來拿到外部類的變量。
    • 在 Activity/Fragment 銷毀的時候請空 MessageQueue 中的消息橘洞。
  • 代碼

    // Handler 弱引用封裝
    public class SafetyHandler<T> extends Handler {
        /**
         * 外部引用, 例如 Activity, Fragment, Dialog, View 等
         */
        private WeakReference<T> mTargetRef;
        public SafetyHandler() {
        }
        public SafetyHandler(T target) {
            this.mTargetRef = new WeakReference<>(target);
        }
        
        public T getTarget() {
            if (isTargetAlive()) {
                return mTargetRef.get();
            } else {
                removeCallbacksAndMessages(null);
                return null;
            }
        }
      
        public void setTarget(T target) {
            this.mTargetRef = new WeakReference<>(target);
        }
      
        private boolean isTargetAlive() {
            return mTargetRef != null && mTargetRef.get() != null;
        }
    }
    
    // 在 Fragment 中使用方法
    // 想重寫 handleMessage 的話捌袜,要創(chuàng)建靜態(tài)內(nèi)部類或者外部類,否則有內(nèi)存泄漏風險
    private static class MyHandler extends SafetyHandler<MyFragment> {
    
        MyHandler(MyFragment fragment) {
              super(fragment);
        }
    
        @Override public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(getTarget() != null) {
                MyFragment fragment = getTarget();
                switch (msg.what) {
                   // 操作 fragment
                }
            }
        }
    }
    
    // 聲明 Handler
    MyHandler handler = new MyHandler(this);
    
    // 使用 Handler
    handler.sendMessage() ...
      
    // onDestroy   
    @Override public void onDestroy() {
          super.onDestroy();
          handler.removeCallbacksAndMessages(null);
    }
    

    ?

四. Why炸枣、Handler 消息機制的原理

這部分從 ActivityThread 的 main 方法出發(fā)虏等,打通整個消息機制的流程,結(jié)合源碼體驗效果更佳适肠。

概述

介紹消息機制的原理前霍衫,我們先來看一下 Handler 與 Message、MessageQueue侯养、Looper 這個四個類的作用

  • Handler:前面已經(jīng)說過敦跌,Handler 負責發(fā)送和處理 Message。
  • Message:消息逛揩,負責傳遞標示(what) 和數(shù)據(jù)(obj) 柠傍;每個 Message 都會通過 target 這個成員變量來綁定一個 Handler,由這個 Handler 來發(fā)送和處理 Message辩稽。
  • MessageQueue:消息隊列惧笛,負責存放有 Handler 發(fā)送過來的消息;每個 Handler 中都有一個 final MessageQueue mQueue搂誉,Handler 發(fā)送消息就是把消息加入這個 MessageQueue 徐紧。
  • Looper:負責不斷的從 MessageQueue 中取出消息然后交給 Handler(Message#target ) 處理;每個 Looper 中都有一個唯一的消息隊列(final MessageQueue mQueue)炭懊,每個 Handler 中都有一個 final Looper mLooper,Handler 中的 MessageQueue 就是來自 Looper拂檩。

注意:每個線程只能有一個 Looper 和 一個 MessageQueue侮腹,可以有多個 Handler,每個 Handler 可以發(fā)送和處理多個 Message稻励。

另外父阻,提到消息機制就不得不說一下 Android 中的主線程(UI 線程)

Android 中的主線程通過 Looper.loop() 進入一個無線循環(huán)中愈涩,不斷的從一個 MessageQueue 取出消息,處理消息加矛,我們每觸發(fā)一個事件履婉,就會向這個 MessageQueue 中添加一個消息,Looper 取出這個消息斟览,Handler 處理這個消息毁腿,正是 Looper.loop() 在驅(qū)動著 Android 應用運行下去 ,這也是為什么 Looper.loop 為什么不會阻塞住主線程的原因(當然前提是在 ActivityThread 的 main 函數(shù) 中調(diào)用)苛茂。

正式進入源碼分析

本源碼分析基于 API 25已烤,以下源碼中刪除了一些無關(guān)的代碼

1、在主線程的入口妓羊,ActivityThread 的 main 方法

public static void main(String[] args) {
        // 準備主線程的 Looer     
        Looper.prepareMainLooper();
        // 創(chuàng)建 ActivityThread
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        // 獲取主線程的 Handler 
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
  
        // 對消息隊列進行無線輪詢胯究,處理消息
        Looper.loop();
        // 一旦跳出循環(huán),拋出異常(Android 不允許跳出主線程的 Looper.loop())
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

-> Looper.prepareMainLooper()

public static void prepareMainLooper() {
     // 準備一個 Looper
     prepare(false);
     synchronized (Looper.class) {
         // main Looper 只能初始化一次躁绸,再次初始化會拋出異常
         if (sMainLooper != null) {
             throw new IllegalStateException("The main Looper has already been prepared.");
         }
         // 獲取 main Looper
         sMainLooper = myLooper();
     }
 }

-> prepare(false)

// 準備一個 Looper裕循,quitAllowed 是否允許 Looper 中的 MessageQueue 退出
// 默認 prepare() 允許退出,主線程這里不允許退出
private static void prepare(boolean quitAllowed) {
 // 先看下 sThreadLocal 是什么
 // static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
 // ThreadLocal:線程本地存儲區(qū)净刮,每個線程都有本地存儲區(qū)域费韭,這個區(qū)域是每個線程私有的,不同的線程不能之間不能彼此訪問
 // 如果 sThreadLocal 中有數(shù)據(jù)庭瑰,拋出異常星持,換句話說 prepare() 這個函數(shù)每個線程只能執(zhí)行一次
 if (sThreadLocal.get() != null) {
     throw new RuntimeException("Only one Looper may be created per thread");
 }
 // 創(chuàng)建 Looper 保存到該線程的 ThreadLocal 中
 sThreadLocal.set(new Looper(quitAllowed));
}

-> new Looper(quitAllowed)

private Looper(boolean quitAllowed) {
 // 在 Looper 創(chuàng)建的時候創(chuàng)建一個消息隊列
 // quitAllowed:消息隊列是否可以退出,主線的消息隊列肯定不允許退出弹灭,所以上面是 prepare(false)
 // quitAllowed 為 false 執(zhí)行 MessageQueue#quit 退出消息隊列時會出現(xiàn)異常
 mQueue = new MessageQueue(quitAllowed);
 // 獲取 Looper 存在于哪個線程
 mThread = Thread.currentThread();
}

-> sMainLooper = myLooper()

public static @Nullable Looper myLooper() {
 // 從 sThreadLocal 中獲取當前線程的 Looper 
 // 如果當前線程沒有掉用 Looper.prepare 返回 null
 return sThreadLocal.get();
}

-> sMainThreadHandler = thread.getHandler();

final Handler getHandler() {
 // 返回 mH
 return mH;
}

// mH 在成員變量的位置 new H()
final H mH = new H();

// H 繼承了 Handler 封裝了一系列關(guān)于 Acitivty督暂、Service 以及其他 Android 相關(guān)的操作
private class H extends Handler 

總結(jié):在主線程的 main 方法中乔遮,會創(chuàng)建主線程的 Looper辨赐、MessageQueue,然后進入 Looper.loop() 循環(huán)中氧急,不斷的取出消息捡鱼,處理消息八回,以此來驅(qū)動 Android 應用的運行。

2驾诈、Handler 的創(chuàng)建缠诅,Handler 的所有構(gòu)造方法都會跳轉(zhuǎn)到下面兩個之一

public Handler(Callback callback, boolean async) {
 // Hanlder 是匿名類、內(nèi)部類乍迄、本地類時管引,如果沒有聲明為 static 則會出現(xiàn)內(nèi)存泄漏的警告
 if (FIND_POTENTIAL_LEAKS) {
     final Class<? extends Handler> klass = getClass();
     if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
         (klass.getModifiers() & Modifier.STATIC) == 0) {
         Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName());
     }
 }
 
 // 獲取 Looper
 mLooper = Looper.myLooper();
 if (mLooper == null) {
     throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
 }
 // 消息隊列,從 Looper 中獲取
 mQueue = mLooper.mQueue;
 // 處理消息的回調(diào)接口
 mCallback = callback;
 // 處理消息的方式是否為異步闯两,默認同步
 mAsynchronous = async;
}

public Handler(Looper looper, Callback callback, boolean async) {
 mLooper = looper;
 mQueue = looper.mQueue;
 mCallback = callback;
 mAsynchronous = async;
}

總結(jié):在 Handler 的構(gòu)造方法中褥伴,Handler 和 Looper谅将、MessageQueue 綁定起來,如果當前線程沒有 Looper 拋出異常(這也是為什么直接在子線程創(chuàng)建 Handler 會出現(xiàn)異常)重慢。

3饥臂、使用 Handler 發(fā)送消息

-> sendMessageAtTime(Message msg, long uptimeMillis)

// 除了 sendMessageAtFrontOfQueue,Handler 所有的 post似踱、sendMessage 都會跳到這個方法
// Message msg: 要發(fā)送的消息
// long uptimeMillis: 發(fā)送消息的絕對時間隅熙,通過 SystemClock.uptimeMillis() 加上我們自己的延遲時間 delayMillis 計算而來
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  MessageQueue queue = mQueue;
  // 消息隊列為空(可能已經(jīng)退出)返回 false 入隊失敗
  if (queue == null) {
    RuntimeException e = new RuntimeException(
      this + " sendMessageAtTime() called with no mQueue");
    Log.w("Looper", e.getMessage(), e);
    return false;  
  }
  // 消息入隊
  return enqueueMessage(queue, msg, uptimeMillis);
}

-> sendMessageAtFrontOfQueue(Message msg)

// 發(fā)送消息到 MessageQueeu 的隊頭
public final boolean sendMessageAtFrontOfQueue(Message msg) {
 MessageQueue queue = mQueue;
 if (queue == null) {
     RuntimeException e = new RuntimeException(
     this + " sendMessageAtTime() called with no mQueue");
     Log.w("Looper", e.getMessage(), e);
     return false;
 }
 // 通過設(shè)置 uptimeMillis 為 0,是消息加入到 MessageQueue 的隊頭
 return enqueueMessage(queue, msg, 0);
}

-> enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

// 所有 Handler 的 post 屯援、sendMessage 系列方法和 runOnUiThread 最終都會調(diào)用這個方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
 // msg.target 是一個 Handler猛们,將 Message 和 Handler 綁定
 // 也就是用哪個 Handler 發(fā)送消息,這個 Message 就和哪個 Handler 綁定
 msg.target = this;
 // 如果設(shè)置了消息處理方式為異步處理
 if (mAsynchronous) {
     msg.setAsynchronous(true);
 }
 // MessageQueue 的方法狞洋,將消息入隊
 return queue.enqueueMessage(msg, uptimeMillis);
}

-> MessageQueue#enqueueMessage(Message msg, long when)

boolean enqueueMessage(Message msg, long when) {
     // Messgae 沒有綁定 Handler 拋出異常
     if (msg.target == null) {
         throw new IllegalArgumentException("Message must have a target.");
     }
     // Messgae 正在使用 拋出異常
     if (msg.isInUse()) {
         throw new IllegalStateException(msg + " This message is already in use.");
     }
 
     synchronized (this) {
         // 消息隊列正在退出弯淘,回收 Message
         if (mQuitting) {
             IllegalStateException e = new IllegalStateException(
                     msg.target + " sending message to a Handler on a dead thread");
             Log.w(TAG, e.getMessage(), e);
             msg.recycle();  // 調(diào)用 Message#recycleUnchecked() 
             return false;
         }
         msg.markInUse();  // 標記 Message 正在使用
         msg.when = when;  // 設(shè)置 Message 的觸發(fā)時間
       
         // mMessages 記錄著 MessageQueue 的隊頭的消息 
         Message p = mMessages;  
         boolean needWake;
         // MessageQueue 沒有消息、Message 觸發(fā)時間為 0吉懊、Messgae 觸發(fā)時間比隊頭 Message 早
         // 總之這個 Message 在 MessageQueue 中需要最先被分發(fā)
         if (p == null || when == 0 || when < p.when) {
             // New head, wake up the event queue if blocked.
             msg.next = p;     // 將以前的隊頭 Message 鏈接在這個 Message 后面
             mMessages = msg;  // 將這個 Message 賦值給 mMessages
             needWake = mBlocked;  // 隊列是否阻塞
         } else {
             // 標記隊列是否阻塞
             needWake = mBlocked && p.target == null && msg.isAsynchronous();
             Message prev;
           
             // 按照時間順序?qū)?Message 插入消息隊列
             for (;;) {
                 prev = p;   // prev 記錄隊頭
                 p = p.next; // p 記錄隊頭的后一個
                 // 隊頭后面沒有消息或者其觸發(fā)事件比要插入的 Message 晚庐橙,跳出循環(huán)
                 if (p == null || when < p.when) {
                     break;
                 }
                 if (needWake && p.isAsynchronous()) {
                     needWake = false;
                 }
             }
             // 將 Message 插入隊列
             msg.next = p; 
             prev.next = msg;
         }

         // We can assume mPtr != 0 because mQuitting is false.
         if (needWake) {
             nativeWake(mPtr);
         }
     }
     return true;
 }

總結(jié):到現(xiàn)在為止,我們的 Handler 已經(jīng)將 Message 發(fā)送到了 MessageQueue借嗽,Message 靜靜的等待被處理态鳖。

4、Looper.loop() 還記得這個方法在 ActivityThread 的 main 調(diào)用了嗎恶导?正是它在不斷處理 MessageQueue 里面的消息浆竭。

public static void loop() {
     // 獲取 Looper.Looper.prepare 準備好的 Looper
     final Looper me = myLooper();
     if (me == null) {
         throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
     }
     // 獲取 Looper 中的消息隊列
     final MessageQueue queue = me.mQueue;

     // 進入無線循環(huán)
     for (;;) {
         // 取出下一條消息
         Message msg = queue.next(); 
         
         // 沒有消息,退出 loop
         // 其實上面 queue.next() 也是一個無限循環(huán)惨寿,獲取到消息就返回邦泄,沒有消息就一直循環(huán)
         if (msg == null) {
             return;
         }

         try {
             // msg.target 實際上就是一個 Handler
             // 獲取到了消息,使用綁定的 Handler#dispatchMessage 分發(fā)消息
             msg.target.dispatchMessage(msg);
         } finally {
             
         }

         // 釋放消息裂垦,把 Message 的各個變量清空然后放進消息池中
         msg.recycleUnchecked();
     }
 }

5顺囊、Handler#dispatchMessage(msg) 消息是如何處理的

public void dispatchMessage(Message msg) {
 // 1
 if (msg.callback != null) {
     handleCallback(msg);
 } else {
     // 2
     if (mCallback != null) {
         if (mCallback.handleMessage(msg)) {
             return;
         }
     }
     // 3. 看到這個方法沒有!就是我們創(chuàng)建 Handler 時重寫的 handleMessage
     // OK 整個流程打通蕉拢!
     handleMessage(msg);
 }
}

總結(jié):流程雖然通了特碳,但是處理 Message 的方法貌似有三種(我標記了序號),而且我們的 handleMessage 的優(yōu)先級最低晕换,其他方法會在什么情況下執(zhí)行呢午乓? 直接說結(jié)論了,調(diào)用 Handler 的 post 系列方法會走序號1的處理届巩,創(chuàng)建 Handler 傳入 Callback 會走序號2 的處理硅瞧。

Handler 機制總結(jié):想使用 Handler 必須要有 Looper,創(chuàng)建 Looper 的時候會創(chuàng)建 MessageQueue恕汇,在 Handler 的構(gòu)造的時候會綁定這個 Looper 和 MessageQueue腕唧,Handler 將 Message 發(fā)送到 MessageQueue 中,Looper.loop() 會不斷的從 MessageQueue 取出消息再交給這個 Handler 處理瘾英。

五. HandlerThread 的使用及源碼解讀

在子線程中能直接創(chuàng)建 Handler 嗎枣接?

  • new Thread(new Runnable() {
        @Override public void run() {
            new Handler().post(new Runnable() {
                @Override public void run() {
                      MLog.i("Handler in " + Thread.currentThread().getName());
                }
            });
        }
    }).start();
    
  • 答案前面提到了是不能,執(zhí)行上面的代碼會出現(xiàn) java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() 這個異常缺谴,異常提示我們但惶,不能再沒有調(diào)用 Looper.prepare() 的線程中創(chuàng)建 Handler。

  • 簡單修改下代碼就可以了湿蛔,給線程準備好 Looper

    new Thread(new Runnable() {
        @Override public void run() {
            // 準備一個 Looper膀曾,Looper 創(chuàng)建時對應的 MessageQueue 也會被創(chuàng)建
            Looper.prepare();
            // 創(chuàng)建 Handler 并 post 一個 Message 到 MessageQueue
            new Handler().post(new Runnable() {
                @Override public void run() {
                      MLog.i("Handler in " + Thread.currentThread().getName());
                }
            });
            // Looper 開始不斷的從 MessageQueue 取出消息并再次交給 Handler 執(zhí)行
            // 此時 Lopper 進入到一個無限循環(huán)中,后面的代碼都不會被執(zhí)行
            Looper.loop();
        }
    }).start();
    
  • 上面的操作 Android 都幫我們封裝好了阳啥,正是 HandlerThread 這個類添谊。

HandlerThread 的簡單使用

// 1. 創(chuàng)建 HandlerThread
handlerThread = new HandlerThread("myHandlerThread") {
    // onLooperPrepared 這個方法子線程執(zhí)行,由線程的 run 方法調(diào)用察迟,可以在里面直接創(chuàng)建 Handler
    @Override protected void onLooperPrepared() {
        super.onLooperPrepared();
        new Handler().post(new Runnable() {
            @Override public void run() {
                // 注意:Handler 在子線程創(chuàng)建斩狱,這個方法也會運行在子線程,不可以更新 UI
                MLog.i("Handler in " + Thread.currentThread().getName());
            }
        });
    }
};

// 2. 準備 HandlerThread 的 Looper 并調(diào)用 onLooperPrepared
handlerThread.start();


// 3. 退出
@Override public void onDestroy() {
    super.onDestroy();
    handlerThread.quit();
}

// 也可以這樣用
// 1. 創(chuàng)建 HandlerThread 并準備 Looper
handlerThread = new HandlerThread("myHandlerThread");
handlerThread.start();

// 2. 創(chuàng)建 Handler 并綁定 handlerThread 的 Looper
new Handler(handlerThread.getLooper()).post(new Runnable() {
    @Override public void run() {
        // 注意:Handler 綁定了子線程的 Looper扎瓶,這個方法也會運行在子線程所踊,不可以更新 UI
        MLog.i("Handler in " + Thread.currentThread().getName());
    }
});

// 3. 退出
@Override public void onDestroy() {
    super.onDestroy();
    handlerThread.quit();
}

HandlerThread 源碼解讀

  • HandlerThread 繼承了 Thread,本質(zhì)是一個擁有 Looper 的線程概荷,因此在 HandlerThread 我們可以直接使用 Handler秕岛。

  • 構(gòu)造方法

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    // 傳入線程的名稱和優(yōu)先級
    // 注意 priority 的值必須來自 android.os.Process 不能來自 java.lang.Thread
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    
  • run 方法:創(chuàng)建子線程的 Looper

    @Override
    public void run() {
        mTid = Process.myTid();
        // 準備一個 Looper
        Looper.prepare();
        synchronized (this) {
            // 獲取 Looper
            mLooper = Looper.myLooper();
            // Looper 獲取成功后,喚醒 getLooper 的 wait
            notifyAll();
      }
      Process.setThreadPriority(mPriority);
        // Looper 準備好的回調(diào)误证,在這個方法里可以使用 Handler 了
        onLooperPrepared();
        // Looper 開始循環(huán)取消息
        Looper.loop();
        mTid = -1;
    }
    
  • getLooper 方法:獲取子線程的 Looper

    public Looper getLooper() {
        // 線程沒有開始或者死亡继薛,返回 null
          if (!isAlive()) {
              return null;
        }
    
        // If the thread has been started, wait until the looper has been created.
        // Looper 的創(chuàng)建時在子線程完成的,而 getLooper 可能會在主線程調(diào)用
        // 當 Looper 沒有創(chuàng)建完成時雷厂,使用 wait 阻塞等待
        // 上面在 Looper 創(chuàng)建好后會 notifyAll 來喚醒 wait
         synchronized(this) {
            while (isAlive() && mLooper == null) {
                try {
                      wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
    
  • quit 和 quitSafely :結(jié)束 Looper 的運行

    // quit
    quit() -> looper.quit() -> mQueue.quit(false);
    // quitSafely 
    quitSafely() -> looper.quitSafely() -> mQueue.quit(true);
    
    // 這兩個方法最終都會調(diào)用到 MessageQueue 的 void quit(boolean safe) 方法
    // 前者會直接移除 MessageQueue 中的所有消息惋增,然后終止 MessageQueue
    // 后者會將 MessageQueue 中已有消息處理完成后(不再接收新消息)終止 MessageQueue
    

    六.參考文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市改鲫,隨后出現(xiàn)的幾起案子诈皿,更是在濱河造成了極大的恐慌,老刑警劉巖像棘,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稽亏,死亡現(xiàn)場離奇詭異,居然都是意外死亡缕题,警方通過查閱死者的電腦和手機截歉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來烟零,“玉大人瘪松,你說我怎么就攤上這事咸作。” “怎么了宵睦?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵记罚,是天一觀的道長。 經(jīng)常有香客問我壳嚎,道長桐智,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任烟馅,我火速辦了婚禮说庭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘郑趁。我一直安慰自己刊驴,他們只是感情好,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布穿撮。 她就那樣靜靜地躺著缺脉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悦穿。 梳的紋絲不亂的頭發(fā)上攻礼,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機與錄音栗柒,去河邊找鬼礁扮。 笑死,一個胖子當著我的面吹牛瞬沦,可吹牛的內(nèi)容都是我干的太伊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼逛钻,長吁一口氣:“原來是場噩夢啊……” “哼僚焦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起曙痘,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤芳悲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后边坤,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體名扛,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年茧痒,在試婚紗的時候發(fā)現(xiàn)自己被綠了肮韧。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖弄企,靈堂內(nèi)的尸體忽然破棺而出超燃,到底是詐尸還是另有隱情,我是刑警寧澤桩蓉,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布淋纲,位于F島的核電站劳闹,受9級特大地震影響院究,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜本涕,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一业汰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧菩颖,春花似錦样漆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至呻右,卻和暖如春跪妥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背声滥。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工眉撵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人落塑。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓纽疟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親憾赁。 傳聞我的和親對象是個殘疾皇子污朽,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

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