1、消息机制的基本原理
各平台的消息机制的原理基本上都是相近的,其中一些主要概念大概有
-
消息发送者:消息的发送方,有时也是消息的处理方
-
消息队列:存放消息的地方
-
消息循环器:消息的调度者,用于不断从消息队列中取出消息,分发给处理者
2、Android中的消息机制
Android中的消息机制主要用于线程间的通讯,主要的流程见下图
3、消息机制的初始化过程
3.1 Handler的创建
Handler对象在创建时会保存当前线程的looper和MessageQueue
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
//1.创建Looper对象
Looper.prepare();
//2.完成Handler、Looper、MessageQueue彼此的绑定
mHandler = new Handler() {
@override
public void handleMessage(Message msg) {
//todoSomething
}
//3.开始死循环
Looper.loop();
}}
public Handler(Callback callback, boolean async) {
....
//get当前线程关联的Looper对象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(}
mQueue = mLooper.mQueue; //MessageQueue
...
}
3.2 Looper的创建
Looper的创建相对简单,创建完会存到对应的线程中
public final class Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
...
private static void prepare(boolean quitAllowed) {
...
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); //创建MessageQueue
mThread = Thread.currentThread();
}
public static Looper myLooper() {
return sThreadLocal.get();
}
}
public class ThreadLocal<T> {
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
return e.value
}
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
}
class Thread implements Runnable {
...
//真正存储线程copy的地方
ThreadLocal.ThreadLocalMap threadLocals = null;
}
3.3 MessageQueue的创建
Looper对象的初始化方法里,首先会新建一个MessageQueue对象。MessageQueue对象的初始化方法通过JNI初始化C++层的NativeMessageQueue对象。并将引用地址返回给Java层保存在mPtr变量中,通过这种方式将Java层的对象与Native层的对象关联在了一起。
public final class MessageQueue {
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
//单链表的数据结构来存储消息列表
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
}
4、消息传递的过程
主流程:
-
Handler通过sendMessage()发送Message到MessageQueue队列,队列中按message需要处理的时间排序;
-
Looper通过loop(),不断提取出达到触发条件的Message,并将Message交给target Handler来处理;
4.1发送消息
- handler发送消息
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
- 消息插入到消息队列,按when顺序插入到链表中
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) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked; //当阻塞时需要唤醒
} else {
// Inserted within the middle of the queue.
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;
}
if (needWake) {
nativeWake(mPtr); //以触发nativePollonce方法结束阻塞
}
}
return true;
}
4.2 取出消息
同一片Looper不断循环取出Message:
首先通过调用Looper的loop方法开始消息监听。loop方法里会调用MessageQueue的next方法。next方法会堵塞线程直到有消息到来为止。
public static void loop() {
final Looper me = myLooper(); //获取TLS存储的Looper对象
if (me == null) {
throw new RuntimeException("No Looper; ....");
}
//获取Looper对象中的消息队列
final MessageQueue queue = me.mQueue;
...
for (;;) { //进入loop的主循环方法
Message msg = queue.next(); //可能会阻塞 May be block
if (msg == null) { //没有消息,则退出循环
return;
}
msg.target.dispatchMessage(msg); //用于分发Message
...
msg.recycleUnchecked(); //将Message放入消息池
}
}
MessageQueue.next()
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
//用来保存注册到消息队列中的空闲消息处理器(IdleHandler)的个数,当消息队列当线程发现它的消息队列没有新的消息需要处理时,不是马上就进入睡眠等待状态,而是先调用注册到它的消息队列中的IdleHandler对象的成员函数queueIdle,以便它们有机会在线程空闲时执行一些操作。
int pendingIdleHandlerCount = -1; // -1 only during first iteration
//用来描述当消息队列中没有新的消息需要处理时,当前线程需要进人睡眠等待状态的时间, -1,表示无限等待,直到被其它线程唤醒;
// 0,继续循环检查队列;
// >0,以nextPollTimeoutMillis的毫米数等待
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands(); //处理binder进程间通讯
}
//for循环不断地调用成员函数nativePollOnce来检查当前线程的消息队列中是否有新的消息需要处理。如果有消息需要处理,则将msg赋给mMessages并返回,否则就一直阻塞
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.
//target为空,则为阻塞message,要找到下一个异步的message
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
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. and return
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;
}
if (mQuitting) {
dispose();
return null;
}
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null;
boolean keep = false;
try {
keep = idler.queueIdle(); //执行idler对应的任务
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;.
nextPollTimeoutMillis = 0;
}
}
next方法通过调用nativePollOnce方法来监听事件。next方法内部逻辑如下所示(简化):
-
进入死循环,以参数timout=0调用nativePollOnce方法。
-
如果消息队列中有消息,nativePollOnce方法会将消息保存在mMessage成员中。nativePollOnce方法返回后立刻检查mMessage成员是否为空。
-
如果mMessage不为空,那么检查它指定的运行时间。如果比当前时间要前,那么马上返回这个mMessage,否则设置> timeout为两者之差,进入下一次循环。
-
如果mMessage为空,那么设置timeout为-1,即下次循环nativePollOnce永久堵塞。 nativePollOnce方法内部利用epoll机制在之前建立的管道上等待数据写入。接收到数据后马上读取并返回结果。
参考资料:
IdleHandler mp.weixin.qq.com/s/KpeBqIEYe…
Android消息机制:zhoujinjian.cc/2017/08/01/…