做多了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方法