1. 程式人生 > >Handler原始碼解析

Handler原始碼解析

  • Handler物件通過執行緒的MessageQueue,允許你傳送或者處理一個Message物件或者一個Runnable物件。
    每一個Handler物件,都與一個單獨的執行緒和執行緒的MessageQueue相關聯。當你建立一個新的Handler物件,
    這個Handler物件便與建立這個handler物件的執行緒和執行緒的MessageQueue相繫結。從這時起,這個handler物件
    將傳入message物件或者runnable物件到MessageQueue中或者從MessageQueue中提取Message物件,並進行相應處理。

  • Handler有兩個主要的作用:
    (1)安排message或者runnable物件在未來的某個時間點執行;
    (2)在其他的執行緒中執行相關邏輯。

  •   在應用程序被建立的時候,MainThread會建立一個MessageQueue,這個MessageQueue將會用於高等級的應用物件(例如:activitys、broadcast reveivers等)和任意一個它建立的windows。也可以建立子執行緒,與主執行緒進行交流。

1.主要程式碼分析

  與Handler機制相關的有幾個類 Looper、MessageQueue、Message和ThreadLocal。

ThreadLocal

  這個類提供執行緒本地變數,即為不同的執行緒變數提供不同的副本。通過get和set方法。ThreadLocal一般為類的私有靜態變數,用於關聯執行緒的state。

例如,下面這個類,用於為每一個執行緒建立唯一id。執行緒的id在第一次呼叫ThreadId.get()方法後被分配,在之後不會變化

import java.util.concurrent.atomic.AtomicInteger;

  public class ThreadId {
      // Atomic integer containing the next thread ID to be assigned
      private static final AtomicInteger nextId = new AtomicInteger(0);

      // Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return nextId.getAndIncrement(); } }; // Returns the current thread's unique ID, assigning it if necessary public static int get() { return threadId.get(); } }

每一個執行緒都持有這個執行緒的ThreadLocal的一個copy的明確引用。直到這個執行緒不在存活或者Threadlocal單例不存在。

1.ThreadLocalMap類

與ThreadLocal類相關的還有一個非常重要的類 ThreadLocalMap。真正的資料儲存是由ThreadLocalMap類完成的。在Thread類中包含一個ThreadLocalMap變數。不過這個變數是可見性是預設的,只能包內訪問到。

Thread.class

/* ThreadLocal values pertaining to this thread. This map is maintained
   * by the ThreadLocal class. */
  ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocalMap類是一個靜態內部類。自定義一個靜態類用於儲存資料的條目。

//繼承WeakReference 弱引用。已ThreadLocal物件為key,Object為value。組成鍵值對。
static class Entry extends WeakReference<ThreadLocal<?>> {
         /** The value associated with this ThreadLocal. */
         Object value;

         Entry(ThreadLocal<?> k, Object v) {
             super(k);
             value = v;
         }
     }
  1. 變數
    // 初始化的容量必須是2的倍數
    private static final int INITIAL_CAPACITY = 16;
    //儲存資料陣列,在必要是可以重新計算陣列長度。陣列長度必須是2的倍數。
    private Entry[] table;
    //儲存資料table容量。
    private int size = 0;
    //判斷是否需要重新計算陣列容量的標誌 預設值0
    private int threshold; // Default to 0
  1. 構造方法
     //建立一個新map,初始值為firstKey和firstValue。只有在至少一個entry放入的時候才會建立。
     ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
         table = new Entry[INITIAL_CAPACITY];//建立儲存資料陣列,大小預設值為16。
         int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);//計算儲存位置。具體演算法沒看懂。
         table[i] = new Entry(firstKey, firstValue);//存入資料
         size = 1;//修改size
         setThreshold(INITIAL_CAPACITY);//修改shreshold變數 為INITIAL_CAPACITY * 2 / 3;
     }
     //另外一個構造方法,引數為ThreadMap,即copy建立一個新的ThreadMap。私有方法
      private ThreadLocalMap(ThreadLocalMap parentMap);
  1. get(hreadLocal
        /**
         *通過ThreadLocal key獲取value。因為儲存Entry是個弱引用和儲存資料位置雜湊可能有碰撞。
         *所以會出現找不到的問題。則交與getEntryAfterMiss()方法來處理。
         */
        private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);//計算儲存位置
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);//未找到則交於getEntryAfterMiss()方法處理。
        }

        //處理無法直接從key獲取value的情況。
        private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
             Entry[] tab = table;
             int len = tab.length;
             while (e != null) {
                 ThreadLocal<?> k = e.get();
                 if (k == key)
                     return e;              //正常返回
                 if (k == null)
                     expungeStaleEntry(i);//從Entry獲取的key,key已經不存在了。則抹去對這個entry的儲存。
                 else
                     i = nextIndex(i, len);//key存在,但是已經不是之前值了。則重新計算位置並存儲entry。
                 e = tab[i];
             }
             return null;
        }
  1. set(ThreadLocal
    private void set(ThreadLocal<?> key, Object value) {
        Entry[] tab = table;
        int len = tab.length;
        int i = key.threadLocalHashCode & (len-1);
        //遍歷陣列,判斷陣列中是否已經存過相同key資料。
        for (Entry e = tab[i];
             e != null;
             e = tab[i = nextIndex(i, len)]) {
            ThreadLocal<?> k = e.get();
            if (k == key) {       // key與陣列中entry的key相等。則替換value。
                e.value = value;
                return;
            }
            if (k == null) {    //陣列entry key為null。則替換這個entry
                replaceStaleEntry(key, value, i);
                return;
            }
        }
        //陣列中不存在這個key 也不存在key為null的情況。則新建entry,並加入陣列。
        tab[i] = new Entry(key, value);
        int sz = ++size;  
        //cleanSomeSlots()方法查詢整個陣列,發現entry為null或者entry.get()為null。則
        //刪除掉這個entry。如果有刪除過entry則返回true。否則返回false。
        //假如沒有刪除過entry 並且增加後的陣列長度 >=threshold變數。則重新計算陣列大小。
        //上面也說過 threshold 變數為陣列長度的三分之二。
        //rehash將會將陣列容量擴大一倍。
        if (!cleanSomeSlots(i, sz) && sz >= threshold)
            rehash();
    }

2.主要方法

  1. initialValue()
//主要用於建立每個執行緒的ThreadLocal值的初始化。上面執行緒Id的例子中重寫了這個方法。
//已實現建立ThreadLocal便初始化Id的功能。這個方法會在Thread第一次呼叫ThreadLocal變數的get()方法時呼叫。
protected T initialValue() {
        return null;
    }

2.get()

public T get() {
    //獲得當前執行緒
     Thread t = Thread.currentThread();
     ThreadLocalMap map = getMap(t);//獲取thread對應的ThreadLocalMap成員變數。
     if (map != null) {
         ThreadLocalMap.Entry e = map.getEntry(this);  //通過key獲取value。
         if (e != null) {
             @SuppressWarnings("unchecked")
             T result = (T)e.value;
             return result;
         }
     }
     return setInitialValue();//如果未找到value則 初始
 }
//初始化執行緒ThreadLocalMap變數
private T setInitialValue() {
    T value = initialValue();   //獲取初始化值,上面的例子中用到這個方法。用於初始化執行緒Id。
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);  //執行緒已存在ThreadLocalMap變數 則加入新初始化的entry。
    else
        createMap(t, value); //執行緒未初始化ThreadLocalMap變數則,初始化並加入新entry
    return value;
}

3.set(T value)

    //執行緒已存在ThreadLocalMap變數,則加入entry。
    //執行緒未初始化ThreadLocalMap變數則,初始化並加入entry。
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

4.如何計算儲存位置。即如何計算entry在ThreadLocalMap中陣列table的下標。

計算公式為:有ThreadLocal的nextHashCode()方法,即從0開始每次呼叫增加 HASH_INCREMENT = 0x61c88647。即0x61c88647的倍數。

//firstKey.threadLocalHashCode = nextHashCode();
//INITIAL_CAPACITY為陣列長度。 預設為16.必須為2的倍數。
//因為INITIAL_CAPACITY為2的倍數。所以公式的表示為:取firstKey.threadLocalHashCode的低幾位。
//搜了下資料 這個數的選取與斐波那契雜湊有關。使用這種方式取的值分佈很均勻。XD.原因不懂。
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);

MessageQueue

*用於Looper使用的快取message列表的初級類,並不是直接通過MessageQueue新增,而是通過與Handler相關聯的
Looper。你可以在當前執行緒中使用Looper.myQueue()方法獲取這個物件。*

主要方法

1.next()

// 取出message的方法。
Message next() {
        、、、
        for (;;) {
            、、、
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {//msg.target即為發出這個message的Handler
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        //當前時間戳小於msg.when,即下一個message還沒有準備好,則設定超市啟動時間。
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 下一個message已經到時間。則將message從連結串列中取出並返回這個message。
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
                、、、
        }
    }

2.enqueueMessage(Message msg, long when)

// 插入一個message。
boolean enqueueMessage(Message msg, long when) {
      、、、
     synchronized (this) {
         、、、
         msg.markInUse();
         msg.when = when;
         Message p = mMessages;
         boolean needWake;
         if (p == null || when == 0 || when < p.when) {
             //佇列中沒有message 或者插入message.when為0 或者小於當前對列頭部message時間戳
             //則將插入message 插入到佇列頭部。
             msg.next = p;
             mMessages = msg;
             needWake = mBlocked;
         } else {
             //判斷message的when並插入到佇列中間。
             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;
 }

Looper

*用於Threand中的訊息迴圈。執行緒預設是沒有這個迴圈的。使用prepare()方法建立。呼叫loop()
方法,開啟迴圈。*
下面例子為建立與Handler互動的Thread。

//eg:
  class LooperThread extends Thread {
      public Handler mHandler;

      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  //在這處理傳入message
              }
          };

          Looper.loop();
      }
  }

主要變數

  //sThreadLocal.get()在呼叫prepare()方法之前,返回null。
  //Looper類中存在靜態的TheadLocal,並且擁有物件中擁有MessageQueue物件。
  static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
  private static Looper sMainLooper;
  final MessageQueue mQueue;
  final Thread mThread;

主要方法

1.loop()

   //迴圈縣城中的MessageQueue。務必在退出的時候呼叫quit方法。
   //把看不懂的程式碼刪掉之後,顯而易見,及從MessageQueue中取message。並呼叫
   //message.target即傳送這個message的handler物件的dispatchMessage()方法。
  public static void loop() {
      final Looper me = myLooper();
      if (me == null) {
          throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
      }
      final MessageQueue queue = me.mQueue;
      、、、
      for (;;) {
          Message msg = queue.next(); // might block
          if (msg == null) {
              return;
          }
          、、、
          try {
              msg.target.dispatchMessage(msg);
              end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
          } finally {
              if (traceTag != 0) {
                  Trace.traceEnd(traceTag);
              }
          }
          、、、
          msg.recycleUnchecked();
      }
  }

2.prepare(boolean quitAllowed)

//新建一個Looper物件存入到這個當前執行緒的ThreadLocalMap變數中。
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

Handler

前面的ThreadLocal、MessageQueue、Lopper已經清楚後,Handler的程式碼就很清晰了。

主要方法

1.dispatchMessage(Message msg)

  //這個方法在Looper的loop()方法中呼叫。
  //可以從這個方法中 看出Handler的回撥邏輯和順序。
  public void dispatchMessage(Message msg) {
      if (msg.callback != null) {
          handleCallback(msg);
      } else {
          if (mCallback != null) {
              if (mCallback.handleMessage(msg)) {
                  return;
              }
          }
          handleMessage(msg);
      }
  }

2.enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

//Handler 所有的傳送訊息的方法,最後都呼叫這個方法。
//該方法只是呼叫了Looper物件中的MessageQueue物件的enqueueMessage();方法。
  private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
      msg.target = this;
      if (mAsynchronous) {
          msg.setAsynchronous(true);
      }
      return queue.enqueueMessage(msg, uptimeMillis);
  }

總結

Handler主要是使用ThreadLocal的在不同執行緒中儲存不同的副本的機制來實現。
在建立Handler物件的時候呼叫Looper.prepare()方法。為當前執行緒新增一個ThreadLocal物件用於
儲存一個Looper物件。而Looper物件中擁有一個MessageQueue物件。這樣就形成了每一個執行緒物件中都儲存一個
Looper物件和MessageQueue物件的結果。因此當Handler傳送資訊時,即向當前執行緒MessageQueue中加入一個message。
而當前執行緒的Looper則從MessageQueue中提取message。並執行Handler傳送message時設定的回撥。因為Looper在各個
執行緒中存在不同的副本。所以實現了跨執行緒通訊。