總結(jié)了一下Handler收發(fā)消息的源碼

Handler

Android整個(gè)ui體系都是圍繞handler的消息機(jī)制,handler可以在子線程發(fā)送消息,在主線程接收處理消息,從而實(shí)現(xiàn)線程的跨越,所有有必要對(duì)其進(jìn)行更加深入的掌握

從一般的使用上看,handler的使用分為發(fā)送消息和接收消息

//實(shí)例化一個(gè)handler對(duì)象
val mHandler = Handler(Looper.getMainLooper()){
       //do some thing in main thread
       false
  }

//通過(guò)handler對(duì)象發(fā)送消息
mHandler.sendEmptyMessage(-1)

消息的發(fā)送

使用還是很熟悉的,發(fā)送端發(fā)送消息,除了發(fā)送空的消息,還可以傳遞一個(gè)帶參數(shù)的Messge對(duì)象,還有延時(shí)發(fā)送消息的api方法

handler發(fā)送消息的方法.png

具體的看一下其中的調(diào)用流程

發(fā)送消息的調(diào)用關(guān)系.png

這里有一些需要注意的點(diǎn)

  • Message的實(shí)例化是通過(guò)Message.obtain()方法,而不是直接new Message()

  • Message對(duì)象會(huì)持有發(fā)送者Handler的引用,并賦值在msg.target 屬性上

  • 在msg入隊(duì)之前,會(huì)判斷mAsynchronous屬性的值,并將msg對(duì)應(yīng)的值進(jìn)行賦值

通過(guò)Handler的send操作,就會(huì)產(chǎn)生一個(gè)附帶有額外信息的Message對(duì)象和相應(yīng)的延遲時(shí)間屬性,那么看看mQueue這個(gè)隊(duì)列是從哪來(lái)的

Handler中的Looper
//Handler
public class Handler {
//mQueue是Handler的一個(gè)成員屬性
 final MessageQueue mQueue;
 final Looper mLooper;

public Handler(@Nullable Callback callback, boolean async) {
       //...省略
       mLooper = Looper.myLooper();
       if (mLooper == null) {
           throw new RuntimeException(
               "Can't create handler inside thread " + Thread.currentThread()
                       + " that has not called Looper.prepare()");
      }
       mQueue = mLooper.mQueue;
       mCallback = callback;
       mAsynchronous = async;
  }

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

Handler中的mQueue和mLooper屬性都是在Handler()構(gòu)造函數(shù)中進(jìn)行賦值引用的,可以看出handler中持有的MessageQueue是從對(duì)應(yīng)的Looper中拿到的,那看一下Looper這個(gè)類

public final class Looper {
   //ThreadLocal的常量
   static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
   private static Looper sMainLooper;  // guarded by Looper.class
   final MessageQueue mQueue;
   final Thread mThread;
   private Printer mLogging;

   public static @Nullable Looper myLooper() {
       return sThreadLocal.get();
  }

   //Looper的創(chuàng)建
   public static void prepare() {
       prepare(true);
  }

  //從ThreadLocal中去獲取對(duì)應(yīng)的Looper,這個(gè)方法只能調(diào)用一次,如果重復(fù)調(diào)用,會(huì)拋出異常
   private static void prepare(boolean quitAllowed) {
       if (sThreadLocal.get() != null) {
           throw new RuntimeException("Only one Looper may be created per thread");
      }
       //會(huì)直接通過(guò)new Looper()的方式創(chuàng)建一個(gè)Looper,并調(diào)用ThreadLocal$set進(jìn)行保存
       sThreadLocal.set(new Looper(quitAllowed));
  }
}

//Looper初始化后調(diào)用的保存方式
public class ThreadLocal<T> {
   public T get() {
     //獲取當(dāng)前線程
       Thread t = Thread.currentThread();
       ThreadLocalMap map = getMap(t);
       if (map != null) {
           ThreadLocalMap.Entry e = map.getEntry(this);
           if (e != null) {
               @SuppressWarnings("unchecked")
               T result = (T)e.value;
               return result;
          }
      }
       return setInitialValue();
  }

   public void set(T value) {
       //獲取當(dāng)前線程
       Thread t = Thread.currentThread();
       //從下面可以Thread類可以看出,每一個(gè)線程會(huì)有一個(gè)ThreadLocalMap的成員屬性
       ThreadLocalMap map = getMap(t);
       if (map != null)
     //Map其實(shí)就是一個(gè)hashmap,里面會(huì)有一個(gè)Entry數(shù)組,Entry保存了一個(gè)鍵值對(duì),而key的類型就是ThreadLocal
           map.set(this, value);
       else
           createMap(t, value);
  }

   //獲取對(duì)應(yīng)線程的ThreadLocalMap
   ThreadLocalMap getMap(Thread t) {
       return t.threadLocals;
  }
}

public class Thread implements Runnable {
   ThreadLocal.ThreadLocalMap threadLocals = null;
}

static class ThreadLocalMap {
  static class Entry extends WeakReference<ThreadLocal<?>> {
           Object value;
           Entry(ThreadLocal<?> k, Object v) {
               super(k);
               value = v;
          }
    }

   private Entry[] table;

   private void set(ThreadLocal<?> key, Object value) {
           Entry[] tab = table;
           //...省略

           tab[i] = new Entry(key, value);
      }
}
小結(jié)一下:Looper的初始化和線程唯一
  • Handler構(gòu)造函數(shù)初始化,傳入了Looper參數(shù)就直接賦值,沒(méi)有傳入就通過(guò)Looper.myLooper()獲取

    構(gòu)造函數(shù)讓Handler的mLooper對(duì)象有值,并且將其中的MessageQueue進(jìn)行傳遞

  • Looper.myLooper()的函數(shù)->調(diào)用其內(nèi)部的 sThreadLocal.get()

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    sThreadLocal是一個(gè)常量,所以得看其內(nèi)部具體的get()邏輯

  • ThreadLocal的set()/get()方法都是先獲取當(dāng)前的線程#Thread.currentThread()#,然后獲取線程內(nèi)部的ThreadLocalMap對(duì)象,這個(gè)Map對(duì)象的key就是ThreadLocal的一個(gè)弱引用

    Looper初始化的時(shí)候,調(diào)用#prepare()方法,會(huì)通過(guò)new Looper()的方法直接實(shí)例化一個(gè)Looper對(duì)象,通過(guò)ThreadLocal的set方法,會(huì)將這個(gè)Looper對(duì)象設(shè)置到對(duì)應(yīng)線程的ThreadLocalMap屬性中,這個(gè)屬性其實(shí)是一個(gè)HashMap,對(duì)應(yīng)的key就是Looper中的常量ThreadLocal,所以線程通過(guò)這個(gè)key會(huì)找到唯一對(duì)應(yīng)的Looper值

    不同的線程對(duì)應(yīng)的ThreadLocalMap是不同的對(duì)象,雖然都是looper中的常量ThreadLocal作為key,但是容器不一樣,所以Looper是線程唯一的

    線程內(nèi)的Handler可以直接sendmessage()嗎
    //在線程里面調(diào)用一個(gè)不傳Looper參數(shù)的Handler構(gòu)造方法
    //調(diào)用Looper.myLooper()->sThreadLocal.get() 由于該線程沒(méi)有初始化設(shè)置Looper對(duì)象,所以這樣寫(xiě)會(huì)崩潰
    new Thread(new Runnable() {
               @Override
               public void run() {
                   //線程里面
    //               Looper.prepare();
    //               Looper.loop();
    
                   Handler mHandler = new Handler(msg -> {
                       return false;
                  }
                  );
    
                   mHandler.sendEmptyMessage(-1);
              }
          }).start();
    
    //錯(cuò)誤信息,線程沒(méi)有調(diào)用Looper.prepare(),所以找不到對(duì)應(yīng)的looper對(duì)象
    java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()

通過(guò)上面的流程,對(duì)Handlder和Looper的關(guān)系有一個(gè)理解了

Handler中有一個(gè)Looper的成員屬性,默認(rèn)情況下,這個(gè)Looper對(duì)象是handler所屬當(dāng)前線程的唯一值

主線程中的Looper
public final class ActivityThread extends ClientTransactionHandler {
       public static void main(String[] args) {
           //...省略各種不相關(guān)的代碼
           Looper.prepareMainLooper();

           Looper.loop();
      }
    }
    
    ->Looper.java
      public static void prepareMainLooper() {
           prepare(false);
           synchronized (Looper.class) {
               if (sMainLooper != null) {
                   throw new IllegalStateException("The main Looper has already been prepared.");
              }
               sMainLooper = myLooper();
          }
      }

app進(jìn)程的main()函數(shù)在ActivityThread類中,從里面可以看出,最終也是調(diào)用了Looper$prepare()方法,對(duì)我們熟悉的主線程進(jìn)行Looper的初始化

Looper中的MessageQueue
public final class Looper {
    private Looper(boolean quitAllowed) {
           mQueue = new MessageQueue(quitAllowed);
           mThread = Thread.currentThread();
      }
    }

從上文可以看到,Looper.prepare()方法中,會(huì)直接new一個(gè)Looper對(duì)象,而Looper的構(gòu)造函數(shù)中,也是直接new了一個(gè)MessageQueue對(duì)象

queue.enqueueMessage

最前面的handler發(fā)送邏輯,最后msg對(duì)象和延遲時(shí)間都是通過(guò)queue.enqueueMessage()進(jìn)行操作的,現(xiàn)在就分析一下怎么入隊(duì)的

public final class MessageQueue {
       private final boolean mQuitAllowed;
       Message mMessages;

     boolean enqueueMessage(Message msg, long when) {
           //target就是handler的引用
           if (msg.target == null) {
               throw new IllegalArgumentException("Message must have a target.");
          }
    
       //子線程可以使用主線程的handler進(jìn)行發(fā)送消息,入隊(duì)的操作需要同步鎖
           synchronized (this) {
               //退出循環(huán)了,后續(xù)入隊(duì)的直接拋異常
               if (mQuitting) {
                   IllegalStateException e = new IllegalStateException(
                           msg.target + " sending message to a Handler on a dead thread");
                   Log.w(TAG, e.getMessage(), e);
                   //消息的回收復(fù)用方法
                   msg.recycle();
                   return false;
              }
    
               msg.markInUse();
               msg.when = when;
               Message p = mMessages;
               boolean needWake;
               //消息是個(gè)鏈表,先取出頭節(jié)點(diǎn)
               //判斷插入鏈表頭部的情況,空隊(duì)列,不需要等待的消息來(lái)了,如果當(dāng)前插入的消息在頭消息更早的時(shí)間
               if (p == null || when == 0 || when < p.when) {
                   // New head, wake up the event queue if blocked.
                   msg.next = p;
                   mMessages = msg;
                   needWake = mBlocked;
              } else {
                   // Inserted within the middle of the queue. Usually we don't have to wake
                   // up the event queue unless there is a barrier at the head of the queue
                   // and the message is the earliest asynchronous message in the queue.
                   needWake = mBlocked && p.target == null && msg.isAsynchronous();
                   Message prev;
                   for (;;) {
                       prev = p;
                       p = p.next;
                       if (p == null || when < p.when) {
                           break;
                      }
                       if (needWake && p.isAsynchronous()) {
                           needWake = false;
                      }
                  }
                   msg.next = p; // invariant: p == prev.next
                   prev.next = msg;
              }
    
               // We can assume mPtr != 0 because mQuitting is false.
               if (needWake) {
                   nativeWake(mPtr);
              }
          }
           return true;
      }
    }
    
    //結(jié)合Message的數(shù)據(jù)結(jié)構(gòu)
    public final class Message implements Parcelable {
     //鏈表的下一個(gè)節(jié)點(diǎn)的引用
     Message next;
     //用來(lái)回收復(fù)用加鎖
     public static final Object sPoolSync = new Object();
     //對(duì)象池也是使用的鏈表
     private static Message sPool;
     private static int sPoolSize = 0;
    
     private static final int MAX_POOL_SIZE = 50;

     public static Message obtain() {
           synchronized (sPoolSync) {
               if (sPool != null) {
                   //取頭節(jié)點(diǎn)進(jìn)行復(fù)用
                   Message m = sPool;
                   //對(duì)象池頭節(jié)點(diǎn)后移一位
                   sPool = m.next;
                   //清理復(fù)用對(duì)象的引用關(guān)系
                   m.next = null;
                   m.flags = 0; // clear in-use flag
                   sPoolSize--;
                   return m;
              }
          }
           //回收池里面沒(méi)有可復(fù)用的對(duì)象,直接new
           return new Message();
      }

       //回收消息對(duì)象
       void recycleUnchecked() {
           //清洗對(duì)象的屬性值
           flags = FLAG_IN_USE;
           what = 0;
           arg1 = 0;
           arg2 = 0;
           obj = null;
           replyTo = null;
           sendingUid = UID_NONE;
           workSourceUid = UID_NONE;
           when = 0;
           target = null;
           callback = null;
           data = null;
    
           //因?yàn)橛墟湵淼氖孜碴P(guān)系,需要加鎖
           synchronized (sPoolSync) {
               //最大回收池大小是50個(gè)
               if (sPoolSize < MAX_POOL_SIZE) {
                   //回收的對(duì)象是插在頭部的,將回收對(duì)象的next指針指向當(dāng)前的頭節(jié)點(diǎn)
                   next = sPool;
                   //頭節(jié)點(diǎn)又引用當(dāng)前回收的對(duì)象
                   sPool = this;
                   sPoolSize++;
              }
          }
      }
    }
Message入隊(duì)過(guò)程
  • Message使用了鏈表的數(shù)據(jù)結(jié)構(gòu),通過(guò)next屬性指向下一個(gè)Message對(duì)象

  • Message中有個(gè)sPool的回收池,回收池就是Message的鏈表隊(duì)列

  • 通過(guò)obtain()方法會(huì)從回收池中去取頭節(jié)點(diǎn),存在會(huì)將回收池后移,如果不存在直接new出一個(gè)對(duì)象

  • 通過(guò)recycleUnchecked()回收Message,也是清空屬性后插入到頭節(jié)點(diǎn)

  • MessageQueue就是一個(gè)Message的鏈表,入隊(duì)的時(shí)候,通過(guò)判斷Message的延遲時(shí)間,進(jìn)行排序插入

    通過(guò)上面分析,handler發(fā)送消息入隊(duì)的操作,最終會(huì)得到一個(gè)按延遲時(shí)間排序的有序鏈表,發(fā)送端基本就分析完了,接下來(lái)看一看消息的消費(fèi)

    消息的消費(fèi)

    上面注意到的點(diǎn),在Looper.prepare()初始化了Looper對(duì)象后,然后不斷的往Looper中的messageQueue發(fā)送消息,代碼層面對(duì)Looper的操作就只有 Looper.loop()方法,所以消息的消費(fèi)就在這里面

###Looper.java
    public static void loop() {
    //獲取當(dāng)前線程的looper
           final Looper me = myLooper();

    //取出對(duì)應(yīng)的MessageQueue
           final MessageQueue queue = me.mQueue;
    
    //消息消費(fèi)的關(guān)鍵代碼,通過(guò)一個(gè)死循環(huán)進(jìn)行輪詢?nèi)∠?           for (;;) {
               Message msg = queue.next(); // might block
               if (msg == null) {
                   // No message indicates that the message queue is quitting.
                   return;
              }
    
               //Looper中如果設(shè)置Printer對(duì)象,消息的消費(fèi)過(guò)程會(huì)輸出日志
               final Printer logging = me.mLogging;
               if (logging != null) {
                   logging.println(">>>>> Dispatching to " + msg.target + " " +
                           msg.callback + ": " + msg.what);
              }

               //...省略
               try {
                   //取出發(fā)送消息的handler,回調(diào)到我們的界面的地方
                   msg.target.dispatchMessage(msg);
              } catch (Exception exception) {
                  //...
              } finally {
                  //...
              }

               if (logging != null) {
                   logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
              }

             //消息回收復(fù)用
               msg.recycleUnchecked();
          }
      }

Message的消費(fèi)還是比較簡(jiǎn)單,通過(guò)Looper的死循環(huán),不斷的從消息隊(duì)列中去取消息,然后將對(duì)應(yīng)的消息分發(fā)出去,就能回調(diào)到我們熟悉的dispatchMessage()方法中了,最后將消息進(jìn)行回收復(fù)用

值得注意的點(diǎn)

  • Looper對(duì)象中如果設(shè)置了Printer屬性,在消息分發(fā)的過(guò)程中,開(kāi)始處理和處理結(jié)束都會(huì)打印日志輸出
public interface Printer {
  /**
    * Write a line of text to the output. There is no need to terminate
     * the given string with a newline.
    */
   void println(String x);
 }

Printer是一個(gè)接口,這個(gè)點(diǎn)可以用來(lái)做簡(jiǎn)單的頁(yè)面FPS監(jiān)控,頁(yè)面的刷新最終也是通過(guò)handler機(jī)制發(fā)送的一個(gè)發(fā)送一個(gè)message進(jìn)行刷新繪制

總結(jié)

終于從前到后從源碼中找到關(guān)鍵代碼,梳理了一遍常用的消息機(jī)制handler的發(fā)送和消費(fèi)過(guò)程,其中涉及到相關(guān)的Looper和Message對(duì)象,記錄了Looper的線程唯一性的原因

一些有所了解還沒(méi)整理總結(jié)的點(diǎn)

  • 對(duì)于looper死循環(huán)的退出(exit())和主線程死循環(huán)沒(méi)有ANR相關(guān)的epoll機(jī)制暫時(shí)沒(méi)有分析,epoll的底層原理理解起來(lái)有點(diǎn)麻煩,通俗一點(diǎn)就是messagequeue隊(duì)列里面沒(méi)有message需要處理的時(shí)候,會(huì)通過(guò)epoll機(jī)制進(jìn)行休眠,然后在需要處理的時(shí)候wake喚醒進(jìn)行消息處理
  • message的target==null的情況下,消息屏障處理異步消息的流程沒(méi)有分析

handler這個(gè)貫穿整個(gè)app的生命周期,也經(jīng)常在群里看到討論Message的復(fù)用機(jī)制,MessageQueue的設(shè)計(jì),Looper的線程唯一性,MessageQueue的池子大小等問(wèn)題,還是得梳理源碼,一下就清晰了,如果有理解錯(cuò)誤的地方,歡迎指導(dǎo)

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

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

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