1. 如何发送消息
一般都会:new Message对象,然后调用 msg.args1或者msg.setData(bundler)设置数据,然后调用 handler.sendMessage(msg)发送消息即可,实例代码如下:
new Thread(new Runnable() {
@Override
public void run() {
Message msg = new Message() ;
msg.arg1 = 1 ;
Bundle bundle = new Bundle() ;
bundle.putString("data" , "data");
msg.setData(bundle);
handler1.sendMessage(msg);
}
}).start();
这里会有几个问题:
1>:handler 把 msg 消息发送到哪里了?
2>:为什么可以在 handler.handleMessage(msg)方法中可以获取到发送的那条消息呢?
下边通过分析源码,来解释这两个问题;
需要知道的是:handler中发送消息的方法有很多,除了sendMessageAtFrontOfQueue()以外,其他发送消息的方法最终都会调用到sendMessageAtTime()方法中;
2. 源码分析
比如从 sendMessage(msg)中进入sendMessageAtTime()方法:
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
/**
* msg:是sendMessage(msg)中发送的 msg对象;
* uptimeMillis:发送消息的时间
*/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
// 把 msg 和 uptimeMillis 传递给 MessageQueue 的 enqueueMessage()方法中
return enqueueMessage(queue, msg, uptimeMillis);
}
MessageQueue: 是消息队列,用于把所有接收到的消息以队列的形式进行排列,提供入队和出队的方法,MessageQueue是在Looper类的 构造方法中 创建的,所以 一个Looper 对应一个 MessageQueue;
enqueueMessage():入队方法,该方法源码如下,这个方法是Handler类中的:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 由于这个方法是 handler 中的,所以 msg.target 就是 handler
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 这里调用 MessageQueue中的 enqueueMessage() 方法
return queue.enqueueMessage(msg, uptimeMillis);
}
boolean enqueueMessage(Message msg, long when) {
// ...省略一些代码
synchronized (this) {
// ...省略一些代码
// 时间,msg被加入到MessageQueue的时间
msg.when = when;
// 当前待处理的消息:MessageQueue没有用集合保存所有消息,而是用 mMessages表示当前待处理的消息
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 根据时间的顺序调用 msg.next,给每一个消息指定它的下一个消息是什么
msg.next = p;
// 此时,就把 mMessages赋值为 新入队的 这条消息,然后把这条消息的 next 指定为刚才的
// mMessages ,到这里就完成了 添加消息到队列头部的操作
mMessages = msg;
} else {
// ... 省略一些代码
}
}
return true;
}
这里要知道:MessageQueue没有用 集合 把所有消息都保存起来,而是用 mMessages表示当前待处理的消息,
所谓入队就是:
把所有消息按时间进行排序,这个时间就是上边的 uptimeMillis,就是根据时间的顺序调用 msg.next,用于给每个消息指定它的下一个消息是什么
出队方法在Looper.loop()中:
public static void loop() {
final Looper me = myLooper();
}
final MessageQueue queue = me.mQueue;
// 这里很重要:在Looper.loop()方法中有一个死循环,会不断的调用消息队列 MessageQueue.next()方法,
// 只要 消息队列中有待处理的消息,就会通过 queue.next()取出待处理的消息 msg,
// 然后 交给 handler.dispatchMessage() 来处理
for (;;) {
// 这个就是消息队列MessageQueue的 出队方法
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
try {
// 从消息队列MessageQueue中 出队的消息 msg:只要上边有消息出队,就把它传递到
// msg.target 的 dispatchMessage()方法中,由上边的 Handler的 enqueueMessage()
// 方法,可知:msg.target就是 handler,也就是说这里调用的就是
// handler的 dispatchMessage()方法;
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
出队方法queue.next():
如果消息队列MessageQueue中存在待处理消息 mMessages,就让这个消息出队,然后让下一个消息 成为mMessages;
如果消息队列MessageQueue中不存在 待处理消息 mMessages,就进入阻塞状态,直到有新的 消息入队;
handler的 dispatchMessage()方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
// 从这里可知:
// 如果mCallback 不为空,就调用mCallback 的 handleMessage()方法;
// 否则,就调用 handler的 handleMessage(msg)方法,然后把 msg传递过去
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
到此就知道:在 handler的 handleMessage(msg)方法中就可以去取出 一开始sendMessage(msg)中的msg了;
3. 为什么用异步消息处理方式可以更新UI?
因为 handler 依赖于创建 handler 对象所在的线程,比如在主线程中创建handler对象,在子线程中不能直接更新UI,需要通过一系列的 发送消息、入队、出队等操作,最后调用到 handler.handleMessage(msg) 方法,此时 handleMessage()方法已经在主线程中,所以就可以在 handleMessage() 方法中更新UI
4. 异步消息处理流程图如下
图片.png
1>:创建handler对象,然后调用 handler.sendMessage(msg)发送消息到 消息队列MessageQueue中;
2>:消息队列MessageQueue中的消息,是按照时间顺序排序的,然后根据时间调用 msg.next,给每个消息指定它的下一个消息是什么,此时就把 mMessages赋值为 新入队的这条消息,然后把 这条消息的 next 指定为刚才的 mMessages,此时完成 添加消息到对头的操作;
3>:然后在 Looper.loop()方法中:有一个 死循环,for(; ;),在这个死循环中会不断的调用 MessageQueue.next()方法,表示不断的从 消息队列中取出消息:
如果有就取出,然后返回 msg对象,然后调用 handler.dispatchMessage(msg)处理,然后调用 handler.handleMessage(msg)处理消息;
如果没有消息:就处于阻塞状态,直到有新的消息入队;
网友评论