handler源码分析

153 阅读9分钟

  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<?> key)
        /**
         *通过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<?> key, Object value)
    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在各个 线程中存在不同的副本。所以实现了跨线程通信。