做多了Android App开发,对这些经常使用,但突然被问起来原理,虽然知道个大概,但真要拿出证据说服,还是得看看Framework代码,才能知道真正的原理,所以,这个博文,我们就直接看代码,讲点实际的东东,也方便大家在日后的分析问题中,更快找的原因。
标题也说的很清楚了,就讲这点东西,说白了,就是线程间的通信,如果做过Win32开发的朋友,应该知道,线程间的通信有几种,比如:内存共享,消息传递等,然而,在Java的世界中,是没有对内存直接操作这个的,看到Message, MessageQueue,大家就知道了,Android是通过消息传递来进行线程间通信的。
一、概要:
Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
Thread:线程,负责调度整个消息循环,即消息循环的执行场所。
二、关系:
图中,反应了MessageQueue是依赖Looper的,而Handler可以使用当前线程的Looper和MessageQueue中的方法,如果有多个Handler在同一线程,那么,都可共用Looper和MessageQueue。
三、代码讲解:
1. Handler.java (文件路径 ./android/os/Handler.java)
public Handler() { 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 = null; }
创建一个Handler,获取当前线程的Looper,并且从Looper中得到MessageQueue的链表header指针。
注: 通常我们都是在UI主线程中创建Handler,因此,获取的Looper为UI主线程的Looper,
如果,我们在子线程中创建Handler,那么,我们一定要在创建子线程Handler之前,
先要创建子线程的Looper,否则会crash,代码如下:
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
在Thread中,我们会调用handler.sendEmptyMessage / sendMessageDelayed / sendMessage / postDelayed / post 等,最终都会走到 sendMessageAtTime 这个
接口,我们看看这个接口究竟做了什么事:
/** * Enqueue a message into the message queue after all pending messages * before the absolute time (in milliseconds) <var>uptimeMillis</var>. * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> * 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) { boolean sent = false; MessageQueue queue = mQueue; if (queue != null) { msg.target = this; sent = queue.enqueueMessage(msg, uptimeMillis); } else { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); } return sent; }
我们看到,首先会去检查MessageQueue是否存在,然后Message将target设置为自己,最终调用MessageQueue.enqueueMessage方法,将Message插入到MessageQueue中,这样就完成了发送消息的过程。
2. Looper.java (文件路径 ./android/os/Looper.java)
初始化Looper类:
private Looper() { mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread(); }
调用Looper.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() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper()); }
在调用Looper.loop()前,必需先调用prepare(),在结束后必需调用quit,释放内存。
接下来,看看Looper的核心代码,loop()是怎么从MessageQueue中取Message,并发送给Handler来处理的:
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } MessageQueue queue = me.mQueue; while (true) { Message msg = queue.next(); // might block if (msg != null) { if (msg.target == null) { // No target is a magic identifier for the quit message. return; } msg.target.dispatchMessage(msg); msg.recycle(); } } }
loop()中,有个while死循环,直到收到msg.target = null的消息,这个消息是谁发的呢?看注释,我们知道是退出消息,那么,当然就是quit()函数发给自己的消息,来退出循环。循环中,不断的从MessageQueue中取下一条Message,如果有且msg.target肯定存在,则调用msg.target 的dispatchMessage(Message)方法,将消息传给目标,而这个目标,正是之前调用enqueueMessage的Handler自己。
我们在回到Handler.java中,看看dispatchMessage的实现:
/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. */ public interface Callback { public boolean handleMessage(Message msg); } /** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { } /** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
在dispatchMessage中,会去检查Message.callback是否存在,也就是说,我们在创建Message时,可以指定一个callback,而不需要调用Handler.handleMessage方法;或者我们实现Callback接口及接口中的handleMessage方法;如果以上两种都没有,那么将调用我们必需实现的Handler.handleMessage方法。
绕了一大圈,最终回到了Handler中来处理!
Message的发送,处理流程,我们已经清楚了,我们看看承载着消息的队列 MessageQueue到底做了什么?比如:我们post 或者 postDelay,MessageQueue是怎么处理这两种消息的优先级的?
3. MessageQueue.java (文件路径 ./android/os/MessageQueue.java)
final boolean enqueueMessage(Message msg, long when) { final boolean needWake; synchronized (this) { if (mQuiting) { RuntimeException e = new RuntimeException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); return false; } else if (msg.target == null) { mQuiting = true; } msg.when = when; Message p = mMessages; if (p == null || when == 0 || when < p.when) { msg.next = p; mMessages = msg; needWake = mBlocked; // new head, might need to wake up } else { Message prev = null; while (p != null && p.when <= when) { prev = p; p = p.next; } msg.next = prev.next; prev.next = msg; needWake = false; // still waiting on head, no need to wake up } } if (needWake) { nativeWake(mPtr); } return true; }
MessageQueue是一个底层类,在初始化时,会去调用native代码,而native代码就是用C/C++来实现的内存,数据结构的管理,从代码中可以看到它实际上,是一个链表结构:如果当前要插入的 Message.when为0,或者小于第一条消息的when,即执行时间,则将当前 Message 置为 MessageQueue的 header;反之,从链表头开始向后搜索,直到找到一条 Message.when的时间是大于当前的时间或者直接插到尾部。
final Message next() { for (;;) { nativePollOnce(mPtr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); final Message msg = mMessages; if (msg != null) { final long when = msg.when; if (now >= when) { mBlocked = false; mMessages = msg.next; msg.next = null; if (false) Log.v("MessageQueue", "Returning message: " + msg); msg.markInUse(); return msg; } else { nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE); } } else { nextPollTimeoutMillis = -1; } } } }
MessageQueue.next()中,也有一个死循环,这个循环,用来不断的检查当前时间与消息队列中第一条Message.when的时间,如果当前时间大于等于消息的时间,则移动 MessageQueue 的头指针至下一条,然后将当前消息返回。
四、小结
1. Handler的处理过程运行在创建Handler的线程里
2. 一个Looper对应一个MessageQueue
3. 一个线程对应一个Looper
4. 一个Looper可以对应多个Handler
5. 不确定当前线程时,更新UI时尽量调用post方法