postdelate到底经历了什么。 handler四大组成部分, Message, MessageQueue, Looper,Handler。
一切要从looper谈起。
Looper里面有两个非常之重要的静态方法,prepare() 和 loop()。 prepare()方法中产生了 MessageQueue 这个消息队列,并且这个消息队列是Looper的一个成员变量,loop()呢,就是采用一个死循环不断的对messagQueue进行遍历,MessageQueue 名字很显然,里面存了Message,但是Message呢里面又持有一个名字是target的对象,类型就是Handler。。这样的话是不是就清晰明了?为什么咱们平常用handler send()了一下一个Message消息,不久之后就会执行Handler里面写好的逻辑了。。就是因为Message 加入了这个MessageQueue里面,被Looper中 loop()方法 找到了这个消息,取出这条message中的target 来执行的。
好接下来上代码Looper里面的代码没几行,最重要的也就是两个静态方法 prepar() 和 loop()。
Looper的成员变量如下:
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal sThreadLocal = new ThreadLocal();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
private Printer mLogging;
看吧,MessageQueue, 还有一个是ThreadLocal,这个类一会儿也要细讲,因为它代表了一个如何把一个变量的作用于局限在线程之间,这种解决方案的一种思想。
再看看他的构造方法:
里面有个叫quitAllowed的布尔类型参数。好像是允不允许messageQueue 退出的标志符。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
然后看下重点方法,Prepare
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}
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));
}
看,new 了一个Looper。那么此时说明已经有MessageQueue了。但是为什么要用 ThreadLocal.set()呢? 费解。不过待会儿一剖析应该就明白了。
还有一个看起来很有用的差不多的方法:
第一行,直接,调用prepare(false),填入的是false!,我想这个方法应该如他的名字一般,为主线程也就是UI线程做的。
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
接下来 大名鼎鼎的loop方法,原理很简单,只要你耐心看:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//进入死循环
for (;;) {
//这里的 next 方法里面,有一些逻辑,当queue中没有元素了,也就是取光了,那就会将当前的线程挂起。
//以便于减少cpu的消耗,方便其他线程执行逻辑。合理分配。
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//这里的target就是message持有的handler对象
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
还有一个我个人比较好奇的方法:
public void dump(Printer pw, String prefix) {
pw.println(prefix + toString());
mQueue.dump(pw, prefix + " ");
}
这里面有一个dump,,这个是MessageQueue里面的方法,留意一下。
ThreadLocal本地线程变量的理解
那么ThreadLocal是干嘛的呢?他还有一个名字叫做本地线程变量。也就是作用域是线程。它怎么就和作用域而且作用域是线程扯上关系了??先说下这个线程作用域在此处的意思。就是一个静态变量a,,假如开了 Thread1, thread2, thread3同时对他做 累加,累减,和不断乘2 的操作,,要的效果是 thread1 打印的结果绝对是a累加的结果, thread2打印结果绝对是累减的结果, thread3打印出来的结果绝对是不断乘2的结果。,看起来这个变量a似乎是分了身,还分了3份,每一份都只为所从属的thread而服务互不干扰。就是这种效果的变量作用域为线程。这就是作用域是线程的变量的栗子吧。 那么ThreadLocal又是如何与线程作用域扯上关系的呢?
看看代码吧:
下图ThreadLocal 的结构。 set(), get(), remove()...咦~~咋和那几个数据结构的类如 list啥的有些像咧。。是不是和存储数据有关呀?而且看看它的几个变量,这名字,reference, hashCounter, hash。我勒个去,,让我想到了哈希,要哈希干嘛嘞?等等,,他还有一个Values 的内部类!额,,
好,,looper类的prepar()静态方法里面不是有一句调用么?
sThreadLocal.set(new Looper(quitAllowed));
那就从这句看起:
/** * Sets the value of this variable for the current thread. If set to * {@code null}, the value will be set to null and the underlying entry will * still be present. * * @param value the new value of the variable for the caller thread. */ public void set(T value) { Thread currentThread = Thread.currentThread(); //得到当前线程 Values values = values(currentThread); //刚才说了,Values是一个内部类,并且是靠单独的方法得到的,还塞了一个当前线程作为参数!!有什么关系么??稍后深扒! if (values == null) { values = initializeValues(currentThread); } values.put(this, value);//put?什么鬼?
我们看到,先取了当前线程,然后调用了ThreadLocal 类的values()方法,得到了一个values变量,之后就是对values变量做的动作了。首先看看values()方法做了啥:
/** * Gets Values instance for this thread and variable type. */ Values values(Thread current) { return current.localValues;//看到没?Thread里面竟然有Values类的成员变量。返回的就是Thread 里面存的这个。 }
至少可以得出了一个信息,,Thread对象 可能会持有ThreadLocal的内部类Values类 的 对象!
然后代码往后走,,if (values == null){......}这堆,又调用了另外一个叫initializeValues()的方法:
/** * Creates Values instance for this thread and variable type. */ Values initializeValues(Thread current) { return current.localValues = new Values(); }
呵呵,,只不过是给Thread. localValues 赋了一个新new 出来的values 对象。
接下来 values.put(this, value),显然这行代码是整个方法的精髓。并且这个values似乎相当重要。那么咱们可以掉头看看这个values到底什么鬼,然后再解析这个put()方法。
Android 线程本地变量<二> ThreadLocal Values源码解析
哇哦,,看来ThreadLocal的源码量被这丫占的差不多了。
Values类变量一览。
/** * * Size must always be a power of 2. */ private static final int INITIAL_SIZE = 16; /** * Placeholder for deleted entries. */ private static final Object TOMBSTONE = new Object(); /** * Map entries. Contains alternating keys (ThreadLocal) and values. * The length is always a power of 2. */ private Object[] table;//直觉告诉我,这个东西八成就是存关键数据的。并且,感觉Values类似乎在描述一种数据结构。 /** Used to turn hashes into indices. */ private int mask; /** Number of live entries. */ private int size; /** Number of tombstones. */ private int tombstones; //表示被删除的实体的数量。 /** Maximum number of live entries and tombstones. */ private int maximumLoad;//阀值,判断是否要进行 rehash 操作。 /** Points to the next cell to clean up. */ private int clean;
构造方法:
/** * Constructs a new, empty instance. */ Values() { initializeTable(INITIAL_SIZE); this.size = 0; this.tombstones = 0; } /** * Used for InheritableThreadLocals. */ Values(Values fromParent) { this.table = fromParent.table.clone(); this.mask = fromParent.mask; this.size = fromParent.size; this.tombstones = fromParent.tombstones; this.maximumLoad = fromParent.maximumLoad; this.clean = fromParent.clean; inheritValues(fromParent); }
第一个构造方法首先initializeTable(16)一下,
/** * Creates a new, empty table with the given capacity. */ private void initializeTable(int capacity) { this.table = new Object[capacity * 2];//创建长度是16 * 2 的Object数组。 this.mask = table.length - 1; this.clean = 0; this.maximumLoad = capacity * 2 / 3; // 2/3 }
好吧,也就是初始化一些值而已。
再看第二个构造方法最后一行调用的方法:
/** * Inherits values from a parent thread. */ @SuppressWarnings({"unchecked"}) private void inheritValues(Values fromParent) { // Transfer values from parent to child thread. Object[] table = this.table; for (int i = table.length - 2; i >= 0; i -= 2) {//呵~ 还一个隔一个的遍历 Object k = table[i]; if (k == null || k == TOMBSTONE) { // Skip this entry. continue; } // The table can only contain null, tombstones and references. Reference> reference = (Reference >) k; // Raw type enables us to pass in an Object below. InheritableThreadLocal key = reference.get(); if (key != null) {//如果数组的中key所在单元格,,key不是空, // Replace value with filtered value. // We should just let exceptions bubble out and tank // the thread creation table[i + 1] = key.childValue(fromParent.table[i + 1]);//那么给它物理地址的下一个地址,赋值 value! } else { //不是的话,,都赋一些没有太大意义的值! // The key was reclaimed. table[i] = TOMBSTONE; table[i + 1] = null; fromParent.table[i] = TOMBSTONE; fromParent.table[i + 1] = null; tombstones++; fromParent.tombstones++; size--; fromParent.size--; } } }
看到这里,你是不是很好奇table这个数组到底怎么存储的key value?我也好奇,我没看源码之前还以为存的是hashMap结构呢,结果不是,,他是纯粹用数组存的信息,,只不过双数索引位置存的都是key,并且key 的下方存的就是其对应的value 而已。。
ThreadLocal.set()最后一句话就是调用这个put()方法的。可以看出,它就是给key找一个位置,或者是以前已经有这个key了,总之就是找位置,然后把这个位置加上key这个元素,然后下一个位置存其对应的value。
/** * Sets entry for given ThreadLocal to given value, creating an * entry if necessary. */ void put(ThreadLocal> key, Object value) { cleanUp(); // Keep track of first tombstone. That's where we want to go back // and add an entry if necessary. int firstTombstone = -1; for (int index = key.hash & mask;; index = next(index)) { //找到下一个数组元素 Object k = table[index]; if (k == key.reference) {//这里让我想到了value的结构!!绝对不是map!!! // Replace existing entry. table[index + 1] = value;//key的下方存储其对应的value return; } if (k == null) { if (firstTombstone == -1) { // Fill in null slot. table[index] = key.reference; //此处存的是 弱引用。 一个被ThreadLocal 持有的成员变量 “reference” table[index + 1] = value; size++; return; } // Go back and replace first tombstone. table[firstTombstone] = key.reference; table[firstTombstone + 1] = value; tombstones--; size++; return; } // Remember first tombstone. if (firstTombstone == -1 && k == TOMBSTONE) { firstTombstone = index; } } }
/** * Gets the next index. If we're at the end of the table, we wrap back * around to 0. */ private int next(int index) { return (index + 2) & mask; }
add也很好理解。。主要是理解了value代码到底是存了个什么结构,怎么存的了。一切代码看起来 so easy!
/** * Adds an entry during rehashing. Compared to put(), this method * doesn't have to clean up, check for existing entries, account for * tombstones, etc. */ void add(ThreadLocal> key, Object value) { for (int index = key.hash & mask;; index = next(index)) { Object k = table[index]; if (k == null) { table[index] = key.reference;//弱引用。。。 table[index + 1] = value; return; } } }
好,现在我们大概可以明白了Values 存了什么东西。。调用set方法的时候,参数是以 key value 进去的,给人一种使用HashMap的误解,但是真正存储,其实就是在一个数组里面倒腾的,,双数索引存key,,单数存value,,对应的两个还得互相挨着。。但是呢,,这个key存的东西已经给指定类型了,它就是一个ThreadLocal 一个弱引用类型的成员变量。并且这个弱引用指向的正是自己。
/** Weak reference to this thread local instance. */ private final Reference也就意味着,,这个key指向的真正对象有可能在特定的情况下,,被回收。至于万一被回收的情况下是怎么处理的建议大家看看源码去。对于ThreadLocal 这个知识,,values 类的普及到此为止已经足够用了。。T>> reference = new WeakReference T>>(this);
回过头咱们再次审视ThreadLocal 的 set 方法:
/** * Sets the value of this variable for the current thread. If set to * {@code null}, the value will be set to null and the underlying entry will * still be present. * * @param value the new value of the variable for the caller thread. */ public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value); }
是不是感觉不一样了?其中的关系在脑海里也有个大概了??也就是存的时候把当前线程里面持有的Values 对象拉出来看看,,如果Thread里面的这个values对象压根就没有,就给他创建一个。。反正最终的下场就是当前的Thread 必须有一个能用的Values 对象。。之后再给Thread 持有的Values对象进行put操作。。values 维护的的正是(ThreadLocal的弱引用, 一个Object对象),元素成对出现两两有序排列的数组。
那么这种关系有与线程作用域有什么关系呢??目前我们起码知道了一些,,就是 每个线程必有对应的valuse对象! 每个线程中有一个。。假设目前有8个线程,如果他们的values对象被初始化完,那么就得有8个Values对象!但是Value是存数据的,存了数据才能发挥其作用。。但是怎么存呢??你会发现,,,调用ThreadLocal 的 set() 方法可以给当前运行的线程中Values对象添加元素,保存一些必要的数据,假设数据是 Data。。那好吧,,以后我要是存,,就调用ThreadLocal.set()方法吧,,但是嘞,你似乎错过了一个细节,就是set方法最后把当前的ThreadLocal 对象 ,也就是this,,传到了key对应的参数里面。。假设当前的8个线程都调用了ThreadLocal.set()方法设置了 一个 Data的value值,,出现的结果是,,这8个线程的Values对象里面都存了
ThreadLocal.get()这个方法,然后以this做为key值,在当前正在执行的线程所持有的Values找出其对应的value值,这里注意的是,看起来8个线程走了同一行代码,,但是返回的data却离奇的成了各自持有的data。。。每个线程的Data都是不一样的,起码物理地址不一样,说明已经是不同的对象了。那么此时对于data这个数据来讲,,是不是作用就是线程作用域了?哈哈。。。。
作用域讲完了,,那么面对Looper里面的有些代码是不是有了些更深刻的理解了?
这里的Looper的作用域,,心里是不是有个底儿了??对没错,,他的作用域就是线程!也就是不同线程调用了Looper.prepare()方法的话,,他们就会持有不同的looper对象,,线程之间不共享,那么looper里面的MessageQueue啥的就更别说了,每个线程都独有,也是不共享!为什么要搞成这个样子呢?我想应该是站在上帝视角的程序员老司机们,,碰到了类似于一种牵扯到不同线程各种处理消息的问题,,还有各种其他的至少我现在不太能想象的问题。作出的策略吧。
自此 ThreadLocal 告一段落!
【源码分析】关于MessageQueue,这一篇就够了!
MessageQueue实则内部维护了一个单向链表
看下成员变量解释写在里面了:
// True if the message queue can be quit. private final boolean mQuitAllowed; //是否允许退出,在主线程下,程序里把这个设置为了false!说明主线程明码说了,不能退! @SuppressWarnings("unused") private long mPtr; // used by native code 是一个native层的指针号。 Message mMessages;//相当于链表头儿 //如果当前没有Message可处理,那就走走这里面的run,执行任务。所以相当于空闲时期执行的自定义任务啥的。 private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); private IdleHandler[] mPendingIdleHandlers; //判断当前是否在退出的标志位 private boolean mQuitting; // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout. private boolean mBlocked; // The next barrier token. // Barriers are indicated by messages with a null target whose arg1 field carries the token. private int mNextBarrierToken;
构造方法,,注意一点是,主线程间接调用到了 MessageQueue(false); 这行代码!
MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit(); }
1 首先MessageQueue里面维护的是一个链表型的数据结构,那么研究就从事情的最开始说起,首先这个链表应该具有添加的功能,然后插入,取出。大概就是这样的。那么看下有关添加新元素的方法:
//看起来就是找给新的message 按照when标准找一个合适的地方插入。 boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; //若是当前连个链表是空的,或者要求的时间很靠前,比链表头还靠前,那就直接将当前消息作为链表头。原先的链表头后移。 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. //如果原先的链表不是空,或者是msg的时间限制比链表头的靠前 //tartget == null 代表的是 当前消息是个屏障消息。msg.isAsynchronous()代表的是判断当前msg 是不是异步的。 needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; //下面就是找这个msg应该插到哪个位置。判断依据是时间。不过,假设messageQueue从第一个开始慢慢添加, //目前基本得到的规律应该是 从链表头开始往下捋,排序下来when应该是递增的。。差不多是0,0,0,100,100,200,300,500,500,500,这样的形式。 //假设新添加的msg.when = 50, 那么就会插入乘, 0,0,0,50,100,100,200,300,500,500,500这个样子。 for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } //如果当前节点是异步消息的话,并且needWake是true,但是假设能全部遍历下来,这个迟早是链表的尾部。 if (needWake && p.isAsynchronous()) { needWake = false; } } //在 p 和 prev 之间插入 新的msg 节点。这应该是加到倒数第二个位置。 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; }
接下来看看怎么取出下一个元素的:
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration //这个值代表时差,当前,和整个队列里面最早的那条msg,时间对比,的时间差,,如果当前时间 //离最早应执行的时间还是早个n毫秒的话,那么好吧,当前没啥可执行的msg,那还干啥活,根据这个 //值作为时间,阻塞个nextPollTimeoutMillis 时间。 int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } //一个native方法,作用是通过Native层的MessageQueue阻塞nextPollTimeoutMillis的时间。。 //这个逻辑很关键。 nativePollOnce(ptr, nextPollTimeoutMillis); 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) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } //到目前为止,msg基本可以确定,如果第一个是同步屏障消息,msg就是第一个异步消息,,但是如果不是的话,msg有可能是同步消息也有可能是异步消息 //从添加msg的方法里面我们大致可以得出这个链表存的大致是 when 由小向大排的链表 if (msg != null) { //那么now < msg.when就意味着,我这整个链表里面,时间最近的那个msg的when值都比现在的时间要大,也就是当前不到执行的时候。 //那么就给nextPollTimeoutMillis 设置一个时间吧,以便于到下次大循环的时候这个值用上,native层好暂停当前线程。 if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. //将阻塞状态的 flag 设置为false。 mBlocked = false; //接着改next指向的对象,把这条msg取出,然后return,这个方法自此结束。。 if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (false) Log.v("MessageQueue", "Returning message: " + msg); return msg; } } else { // No more messages.如果没有Msg nextPollTimeoutMillis = -1;//那就设置为-1,会阻塞 } // Process the quit message now that all pending messages have been handled. //如果当前队列正在退出,,则进行销毁回收工作。最终调用的是native层面的。 if (mQuitting) { dispose(); return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. //没有退出的话,并且走到这里已经表明起码目前没有要执行的msg,那么执行一些程序员自己定义的空闲时间执行的任务。 if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf("MessageQueue", "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } }
从代码可以看出 MessageQueue看名字像是一个队列,但是事实上呢?还不是维护着一个按照时间由小到大进行排序的链表,,包括next 下一个元素的取出,,都不一定是从链表的尾部找的,而是根据时间从中间掐出来msg的。并且这个msg还得是异步消息。。那么不禁反问了,难道还有同步消息?这俩啥时候用?为什么忽略了同步消息啊,那被忽略这同步消息岂不是没机会执行了?
那么里面有个判断要注意一下,msg.target == null 是什么意思。目前已经知道,msg的target其实就是一个handler对象,但是,什么情况下它会是null呢??message不就是用过handler方法send出去的么?其间的联系应该不会导致handler是null啊。。但是看代码还真的有一种情况。。就是屏障消息的设置。所以在这里先讲一下屏障消息。然后再看Messagequeue的删除逻辑吧。本应该现在讲删除的。
根据时间添加一个屏障消息
//通过代码可以得知,这是一个强行在链表根据when来插入一条新的消息。并且更加值得注意的是 //这个消息里面并没有设置target。 //所以他的意思是设置一个消息屏障,在这个消息屏障所有之后的消息,即使是到了时间 //也不会执行相应的异步消息。屏障嘛,要对得起这个词 int enqueueSyncBarrier(long when) { // Enqueue a new sync barrier token. // We don't need to wake the queue because the purpose of a barrier is to stall it. synchronized (this) { final int token = mNextBarrierToken++; //首先先获取一个message对象。但是以下没有设置target(handler类对象) final Message msg = Message.obtain(); msg.markInUse(); msg.when = when; msg.arg1 = token; //这里这里,该设置的都设置了,唯独少了msg.target Message prev = null; Message p = mMessages; if (when != 0) { //挨个找Msg,直到 msg 的时间比给定的这个时间靠前。 //间接可以得知,这个queue 的顺序,,应该是从头到尾,when从大到小。 while (p != null && p.when <= when) { prev = p; p = p.next; } } //如果prev不是链表头的话,那么把这个msg添加到 prev 到 p 的中间去 if (prev != null) { // invariant: p == prev.next msg.next = p; prev.next = msg; } else {//如果prev是链表头的话,那么直接赋给链表头 msg.next = p; mMessages = msg; } return token;//此处返回了一个token值 } }
删除token对应屏障的代码:
void removeSyncBarrier(int token) { // Remove a sync barrier token from the queue. // If the queue is no longer stalled by a barrier then wake it. synchronized (this) { Message prev = null; Message p = mMessages; //通过while循环找到那条屏障消息 while (p != null && (p.target != null || p.arg1 != token)) { prev = p; p = p.next; } if (p == null) { throw new IllegalStateException("The specified message queue synchronization " + " barrier token has not been posted or has already been removed."); } final boolean needWake; //到了这里,p 当前就是那条屏障消息 if (prev != null) { //通过改变指针的方式将指向P的引用断掉,从而使p成为一个垃圾回收对象 prev.next = p.next; needWake = false; } else { //但是还有一种情况就是这个屏障就在链表头处 mMessages = p.next; needWake = mMessages == null || mMessages.target != null; } p.recycleUnchecked(); // If the loop is quitting then it is already awake. // We can assume mPtr != 0 when mQuitting is false. if (needWake && !mQuitting) { nativeWake(mPtr); } } }但是屏障消息的具体作用又是什么?具体在哪里有体现呢?迷惑迷惑,查资料说是和View有关。
找到原因了,原来咱们平常发的消息只是同步消息,但是对于message来说是有同步异步区分的。但是这个异步消息咱们貌似不太接触,但是系统中,尤其是View的相关事件中会大量的用到异步消息。View可是一个相当重要的地方,一旦来了消息是属于被尽快处理的哪一类,但是MessageQueue的数据顺序呢就是when由小到大的。同步异步都是按着这个存起来的。那怎么做到来了异步消息尽快给处理呢?好吧,我设立一个类似于一种代表要立马执行异步消息的符号添加到里面不就得了? 只要有这条消息,就代表着当前我要忽略同步消息,直抄异步消息去,去执行他,,如果不需要执行异步消息了,改为按顺序执行的时候,我就把这个符号撤掉。。同步屏障消息大概也就是这样个思路吧。
接下来关于删除MessageQueue里面的消息,有好几种方法
//很简单的删节点逻辑删除 符合三个参数的所有message void removeMessages(Handler h, int what, Object object) { if (h == null) { return; } synchronized (this) { Message p = mMessages; // Remove all messages at front. //如果恰好要删除的就是在头部,那就改头呗。 while (p != null && p.target == h && p.what == what && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; p.recycleUnchecked(); p = n; } // Remove all messages after front. //但是如果不是在头部的话,那就改这个元素的前一个节点的next指针指向 当前节点的下一个元素 while (p != null) { Message n = p.next; if (n != null) { if (n.target == h && n.what == what && (object == null || n.obj == object)) { Message nn = n.next; n.recycleUnchecked(); p.next = nn; continue; } } p = n; } } }
//这个方法和上个方法的逻辑基本重复 void removeMessages(Handler h, Runnable r, Object object) { if (h == null || r == null) { return; } synchronized (this) { Message p = mMessages; // Remove all messages at front. while (p != null && p.target == h && p.callback == r && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; p.recycleUnchecked(); p = n; } // Remove all messages after front. while (p != null) { Message n = p.next; if (n != null) { if (n.target == h && n.callback == r && (object == null || n.obj == object)) { Message nn = n.next; n.recycleUnchecked(); p.next = nn; continue; } } p = n; } } }
//感觉和上面的方法一模一样啊 void removeCallbacksAndMessages(Handler h, Object object) { if (h == null) { return; } synchronized (this) { Message p = mMessages; // Remove all messages at front. while (p != null && p.target == h && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; p.recycleUnchecked(); p = n; } // Remove all messages after front. while (p != null) { Message n = p.next; if (n != null) { if (n.target == h && (object == null || n.obj == object)) { Message nn = n.next; n.recycleUnchecked(); p.next = nn; continue; } } p = n; } } }
//遍历所有的元素 使其recycleUnchecked(),并将头部节点设置为null, private void removeAllMessagesLocked() { Message p = mMessages; while (p != null) { Message n = p.next; p.recycleUnchecked(); p = n; } mMessages = null; }
//找到比当前时间还靠后的那个元素,并回收所有气其后的元素。将这个点置的next置为null,断掉引用链 private void removeAllFutureMessagesLocked() { final long now = SystemClock.uptimeMillis(); Message p = mMessages; if (p != null) { if (p.when > now) { removeAllMessagesLocked(); } else { Message n; for (;;) { n = p.next; if (n == null) { return; } if (n.when > now) { break; } p = n; } p.next = null; do { p = n; n = p.next; p.recycleUnchecked(); } while (n != null); } } }
其中这个recycleUnchecked()方法被使用的次数是很频繁的,他是Message里面的,下面看下大致逻辑
/** * Recycles a Message that may be in-use. * Used internally by the MessageQueue and Looper when disposing of queued Messages. */ //将属性设置为一些无意义的值,并且,赋给spool,再让spool的next指针指向自己。这样 //spool因为有指向自己的指针,所以可以避免被垃圾回收机制 回收。 //另外spool是一个静态变量,划重点,在obtain()方法里使用到,目的就是为了减少new的次数。 void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
其余的方法:
void quit(boolean safe) { if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { return; } mQuitting = true; if (safe) { removeAllFutureMessagesLocked(); } else { removeAllMessagesLocked(); } // We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr); } }
看了一下大致的源码,首先引入眼帘的最显眼的当属那些obtain()静态方法!重载有N个,但是最重要的当属下面一个,因为其他的,也不过是先调用这个方法,再进行一些其他属性的设置而已:
/** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. */ //逻辑的主要含义相当于copy了一个spool对象,然后把其next指向空。。 //但是spool实在无意义的时候,还是得自己new。但是这样做,能够减少new的次数。 public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); }那么spool在什么时候会被赋值呢?就在前面咱们讲过的一个方法里,再贴一次。
//将属性设置为一些无意义的值,并且,赋给spool,再让spool的next指针指向自己。这样 //spool因为有指向自己的指针,所以可以避免被垃圾回收机制 回收。 //另外spool是一个静态变量,在obtain()方法里使用到,目的就是为了减少new的次数。 void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
这个方法的名称很简单粗暴,,未经检验回收,,那么与其对立的应该还有个经检验之后回收的,,果真有:
/** * Return a Message instance to the global pool. ** You MUST NOT touch the Message after calling this function because it has * effectively been freed. It is an error to recycle a message that is currently * enqueued or that is in the process of being delivered to a Handler. * */ public void recycle() { if (isInUse()) { if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); } return; } recycleUnchecked(); }
其余的Message也很简单,无非就是设置一些数据,设置是不是异步,设置target 或者 callback, 这之类的。当然还有相关的获取方法,不过实现都是很简单的。但是 Message 有一个关于序列化的静态常量,并且还有一点是,Message本身就实现了Parcelable 接口。对于这个,以前面试的时候被问到过,但是我答得不是很明白。这个要是有时间,新写一篇关于Parcelable 的。
其实前面的如果都懂了,你会发现Handler的源码如此简单!
因为handler的代码写的有点散,,变量居然写到了后面,所以截图展示他的变量们
划重点,mLooper!!!这个东西很显然肯定会是线程里独有一份的那个looper,而不是自己new出来的。那looper里有啥呀??重重重点,,looper有MessageQueue!!大致可以猜出要他干嘛来的吧。
另外 mQueue, 不是looper里有吗?干嘛还搞一个变量?我想是因为写代码方便吧,因为mQueue里面有不少关于message添加的逻辑,删除逻辑之类的。咱们什么sendMessage里面,,实现不信不用这套逻辑。其他的也没啥讲的。
接下来,就是他的方法们,,一个构造方法竟然出现了7个重载,,真是有心了!贴出一个代码量稍微大稍微有意义的一个构造方法:
//也就是一些成员变量的赋值 public Handler(Callback callback, boolean async) { 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()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
其他的构造方法都调用了这处。但是有意思的有几处。
/** * Default constructor associates this handler with the {@link Looper} for the * current thread. * * If this thread does not have a looper, this handler won't be able to receive messages * so an exception is thrown. */ public Handler() { this(null, false); }
/** * Constructor associates this handler with the {@link Looper} for the * current thread and takes a callback interface in which you can handle * messages. * * If this thread does not have a looper, this handler won't be able to receive messages * so an exception is thrown. * * @param callback The callback interface in which to handle messages, or null. */ public Handler(Callback callback) { this(callback, false); }
/** * Use the provided {@link Looper} instead of the default one. * * @param looper The looper, must not be null. */ public Handler(Looper looper) { this(looper, null, false); }
/** * Use the provided {@link Looper} instead of the default one and take a callback * interface in which to handle messages. * * @param looper The looper, must not be null. * @param callback The callback interface in which to handle messages, or null. */ public Handler(Looper looper, Callback callback) { this(looper, callback, false); }
有三个构造方法,默认第二个参数为false。也就是最终mAsychronous参数是false。。这个参数以后可是会随着设置到Message里面去的。。也就是相当于一半的构造方法,默认了发送的消息是同步消息!!!再想想咱们平常如何使用的handler??是不是忽然发现,平常你设置的handler发送的都是同步消息??
另外的一些构造方法,也就是设置一下里面的值而已!不需要多讲了。
接下来,喜闻乐见的一个方法:
/** * Subclasses must implement this to receive messages. */ //一个著名的方法,是不是发现自己没少重写这个方法? public void handleMessage(Message msg) { }
啥时候被调用捏?handler里面的dispatchMessage()调用到了这个方法。这个方法是不是很熟悉??哈哈Looper里有!!就是在loop()方法里面的,得到了一个消息之后,调用msg.target.dispatchHandler()是不是发现相互之间的关系就像一个圈圈,终于画圆满了。
/** * Handle system messages here. */ //此处调用了重写的handleMessage()方法,有意思的是,如果msg有callback,就会执行callback里面的 //如果 handler有callback,会执行handler的calback,,实在没招了才执行到handleMessage()方法。 //有优先级啊说明 public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
证据:
好继续看代码,接下来就是一系列obtainMessage()方法,实际上也就是调用了一波Message的obtain()静态方法而已。
/** * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this). * If you don't want that facility, just call Message.obtain() instead. */ public final Message obtainMessage() { return Message.obtain(this); }
那么看下重量级的东西吧,,发消息相关代码。
/** * Pushes a message onto the end of the message queue after all pending messages * before the current time. It will be received in {@link #handleMessage}, * in the thread attached to this handler. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ //看到实际上也就是调用了另一个方法,那么继续追, public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }
/** * Enqueue a message into the message queue after all pending messages * before (current time + delayMillis). You will receive it in * {@link #handleMessage}, in the thread attached to this handler. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ //艾玛,,又调用到了另外的一个方法继续继续往里追 public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }
/** * Enqueue a message into the message queue after all pending messages * before the absolute time (in milliseconds) uptimeMillis. * The time-base is {@link android.os.SystemClock#uptimeMillis}. * Time spent in deep sleep will add an additional delay to execution. * You will receive it in {@link #handleMessage}, in the thread attached * to this handler. * * @param uptimeMillis The absolute time at which the message should be * delivered, using the * {@link android.os.SystemClock#uptimeMillis} time-base. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public boolean sendMessageAtTime(Message msg, long uptimeMillis) { //mQueue实则就是looper里面的那个,也就是整个线程只有一份的那个 MessageQueue queue = mQueue; 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); }
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { //给msg对象加上target,由此可见,咱们通过sendmessage()方法过来的,都是有target的正常消息,不是同步屏障消息哈 msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } //呵呵呵呵呵。。。。。。。入数据 return queue.enqueueMessage(msg, uptimeMillis); }
好吧,我想已经够全面的了,就是辗转辗转再辗转,,,哈哈哈
接下来看看发送空消息。。说实话我纳闷为什么会发送空的消息。
/** * Sends a Message containing only the what value. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendEmptyMessage(int what) { return sendEmptyMessageDelayed(what, 0); }
看看上面的英文注释,,有个placed 这个词很有意思,,替换!!!这个message里面只有what 和 target欧。来来我们再回顾一下,一个饱满的Message 应当具有哪些,以下是Message内部定义的变量。
/** * Sends a Message containing only the what value, to be delivered * after the specified amount of time elapses. * @see #sendMessageDelayed(android.os.Message, long) * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); }
来来回回也就是这些操作。。感觉emptyMessage 与 普通message的区别,也就是enmptyMessage 绝对不饱满,但是普通的message有可能不饱满。
其余的就是一些remove的方法,间接调用到了messageQueue 的 remove。没啥好像的。
来来来,最后复制粘贴一张图祭天,到了这里估计一看经明白了: