關(guān)于Android那些事--handler

簡述

Handler 與 Message、MessageQueue、Looper 一起構(gòu)成了 Android 的消息機(jī)制,主要用來進(jìn)行線程間通信來達(dá)到與用戶進(jìn)行交互,View 的繪制、點(diǎn)擊事件、Activity 的生命周期回調(diào)等作用。

  • Handler:Handler 負(fù)責(zé)發(fā)送和處理 Message;創(chuàng)建時先獲取默認(rèn)或傳遞來的 Looper 對象,并持有 Looper 對象包含的 MessageQueue,發(fā)送消息時使用該 MessageQueue 對象來插入消息并把自己封裝到具體的 Message 中。
  • Message:消息,負(fù)責(zé)傳遞標(biāo)示(what) 和數(shù)據(jù)(obj) ;每個 Message 都會通過 target 這個成員變量來綁定一個 Handler,由這個 Handler 來發(fā)送和處理 Message。
  • MessageQueue:消息隊(duì)列,負(fù)責(zé)存放有 Handler 發(fā)送過來的消息;每個 Handler 中都有一個 final類型的MessageQueu,Handler 發(fā)送消息就是把消息加入這個 MessageQueue 。
  • Looper:負(fù)責(zé)不斷的從 MessageQueue 中取出消息然后交給 Handler(Message#target ) 處理;每個 Looper 中都有一個唯一的消息隊(duì)列(MessageQueue),每個 Handler 中都有一個 final類型的Looper來進(jìn)行消息的無限循環(huán),Handler 中的 MessageQueue 就是來自 Looper。如果獲取的 MessageQueue 沒有消息時,便阻塞在 loop 的queue.next() 中的 nativePollOnce() 方法里,反之則喚醒主線程繼續(xù)工作,之后便使用 Message 封裝的 handler 對象進(jìn)行處理

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

注意事項(xiàng)

  • 使用Handler時可能引起內(nèi)存泄漏
    • Java 中非靜態(tài)內(nèi)部類和匿名內(nèi)部類會持有外部類的引用
      • 非靜態(tài)的內(nèi)部 Handler 子類、匿名 Handler 子類會持有外部類的引用(Activity),而 Handler 可能會因?yàn)橐却幚砗臅r操作導(dǎo)致存活時間超過 Activity,或者消息隊(duì)列中存在未被 Looper 處理的 Message ,而 Message 會持有 Handler 的引用。于是,在 Activity 退出時,其引用還是被 Handler 持有,導(dǎo)致 Activity 無法被及時回收,造成內(nèi)存泄露。
    • Handler 的生命周期比外部類長
      • 非靜態(tài)的內(nèi)部 Runnable 子類、匿名 Runnable 子類 post 到任意 Handler 上時,Runnable 其實(shí)是 Massage中的 Callback,持有 Message 引用,如果這個 Massage 在消息隊(duì)列還沒有被處理,那么就會造成 Runnable 一直持有外部類的引用而造成內(nèi)存泄露。
  • 解決方案
    • 根本思路:讓使用handler的對象在自己生命周期內(nèi)使用handler,不使用及時移除Messages
    • 通過靜態(tài)內(nèi)部類或者外部類來聲明 Handler 和 Runnable。
    • 通過弱引用來拿到外部類的變量。
    • 在 Activity/Fragment 銷毀的時候請空 MessageQueue 中的消息。

源碼分析

在主線程的入口,ActivityThread 的 main 方法

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

逐步分析
==> Looper.prepareMainLooper()

public static void prepareMainLooper() {
     // 準(zhǔn)備一個 Looper, false 不允許退出
     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)

// 準(zhǔn)備一個 Looper,quitAllowed 是否允許 Looper 中的 MessageQueue 退出
// 默認(rèn) 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));
}

總結(jié):用 ThreadLocal 來保存該線程的 Looper 對象。ThreadLocal 可以看作是一個用來儲存數(shù)據(jù)的類,類似 HashMap、ArrayList等集合類,它存放著屬于當(dāng)前線程的變量。

==> new Looper(quitAllowed)

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

==> sMainLooper = myLooper()

public static @Nullable Looper myLooper() {
 // 從 sThreadLocal 中獲取當(dāng)前線程的 Looper 
 // 如果當(dāng)前線程沒有調(diào)用 Looper.prepare 返回 null, 無準(zhǔn)備無對象
 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,然后進(jìn)入 Looper.loop() 循環(huán)中,不斷的取出消息,處理消息,以此來驅(qū)動 Android 應(yīng)用的運(yùn)行。

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()");
 }
 // 消息隊(duì)列,從 Looper 中獲取
 mQueue = mLooper.mQueue;
 // 處理消息的回調(diào)接口
 mCallback = callback;
 // 處理消息的方式是否為異步,默認(rèn)同步
 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 綁定起來,如果當(dāng)前線程沒有 Looper 拋出異常(這也是為什么直接在子線程創(chuàng)建 Handler 會出現(xiàn)異常)。
故:

  • 說明了Handler 必須在有 Looper 的線程中使用。
  • Handler 的 MessageQueue 對象是由當(dāng)前線程 Looper 的 MessageQueue 對象賦值的。
  • Handler 在創(chuàng)建時綁定了當(dāng)前線程 Looper 的 MessageQueue 對象。
  • 對于傳遞 Looper 對象創(chuàng)建 Handler 的情況下,傳遞的 Looper 是哪個線程的,Handler 綁定的就是該線程。

3、使用 Handler 發(fā)送消息
==> sendMessageAtTime(Message msg, long uptimeMillis)

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

==> sendMessageAtFrontOfQueue(Message msg)

// 發(fā)送消息到 MessageQueeu 的隊(duì)頭
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 的隊(duì)頭
 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 的方法,將消息入隊(duì)
 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) {
         // 消息隊(duì)列正在退出,回收 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();  // 標(biāo)記 Message 正在使用
         msg.when = when;  // 設(shè)置 Message 的觸發(fā)時間
       
         // mMessages 記錄著 MessageQueue 的隊(duì)頭的消息 
         Message p = mMessages;  
         boolean needWake;
         // MessageQueue 沒有消息、Message 觸發(fā)時間為 0、Messgae 觸發(fā)時間比隊(duì)頭 Message 早
         // 總之這個 Message 在 MessageQueue 中需要最先被分發(fā)
         if (p == null || when == 0 || when < p.when) {
             // New head, wake up the event queue if blocked.
             msg.next = p;     // 將以前的隊(duì)頭 Message 鏈接在這個 Message 后面
             mMessages = msg;  // 將這個 Message 賦值給 mMessages
             needWake = mBlocked;  // 隊(duì)列是否阻塞
         } else {
             // 標(biāo)記隊(duì)列是否阻塞
             needWake = mBlocked && p.target == null && msg.isAsynchronous();
             Message prev;
           
             // 按照時間順序?qū)?Message 插入消息隊(duì)列
             for (;;) {
                 prev = p;   // prev 記錄隊(duì)頭
                 p = p.next; // p 記錄隊(duì)頭的后一個
                 // 隊(duì)頭后面沒有消息或者其觸發(fā)事件比要插入的 Message 晚,跳出循環(huán)
                 if (p == null || when < p.when) {
                     break;
                 }
                 if (needWake && p.isAsynchronous()) {
                     needWake = false;
                 }
             }
             // 將 Message 插入隊(duì)列
             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 準(zhǔn)備好的 Looper
     final Looper me = myLooper();
     if (me == null) {
         throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
     }
     // 獲取 Looper 中的消息隊(duì)列
     final MessageQueue queue = me.mQueue;

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

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

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

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

public void dispatchMessage(Message msg) {
 // 調(diào)用 Handler 的 post 系列方法執(zhí)行handleCallback(msg)
 if (msg.callback != null) {
     handleCallback(msg);
 } else {
     // 創(chuàng)建 Handler 傳入 Callback
     if (mCallback != null) {
         if (mCallback.handleMessage(msg)) {
             return;
         }
     }
     // 3. 創(chuàng)建 Handler 時重寫的 handleMessage
     handleMessage(msg);
 }
}

從這就可以看出消息分發(fā)的優(yōu)先級:

  • Message 的回調(diào)方法:message.callback.run(); 優(yōu)先級最高;
  • Handler 的回調(diào)方法:mCallback.handleMessage(msg)優(yōu)先級次于上方;
  • Handler 的回調(diào)方法:handleMessage() 優(yōu)先級最低。

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容