原来一直觉得源码都看过了,应该也差不多了,今天用到了postDelay,然后问自己postDelay和sendMessage有什么区别,是如何处理postDelay的Runnable的,然后就没有然后了;
如果你也有这个疑问那就跟我往下一起看,如果你知道,请看我说的是不是你想的,或者我说的有哪些不足之处,
1、消息入队
1.postDelayed消息入队
我们先往里追源码顺便再熟悉一下handler的知识点
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
//这里我们看到postDelayed还是调用自身的sendMessageDelayed,我们接着去看sendMessageDelayed
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
复制代码
这里我们看到sendMessageDelayed其实也是接收一个Message,那我们回到postDelayed看看这个Message如何处理Runnable的
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
复制代码
这里我们看到是把Runnable放到了Message.callback中,然后把Message传递到sendMessageDelayed中,这样就通了
private static Message getPostMessage(Runnable r) {
//Message.obtain() 从全局池返回一个新的Message实例。使我们在许多情况下避免分配新对象
Message m = Message.obtain();
m.callback = r;
return m;
}
复制代码
现在我们接着看sendMessageAtTime
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
//如果mQueue为空说明没有调用prepare,这个我们下面再说
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);
}
复制代码
2.sendMessage消息入队
public final boolean sendMessage(@NonNull Message msg) {
//在此我们发现postDelayed()只是把Runnable封装了一下,走的逻辑还是一样的,
return sendMessageDelayed(msg, 0);
}
复制代码
到此我们看到消息入队已经完毕,我们接着去看消费。
2、消费
我直接进Looper.loop(),顺便简单梳理一下流程
这里补一个小知识点,
线程和 Handler Looper MessageQueue 的关系是一个线程对应一个 Looper 对应一个 MessageQueue 对应多个 Handler,既然是一个MessageQueue对应多个Handler,会不会Handler_A发消息Handler_B会同时接到,答案是不会的,下面会有说。
1.获取当前线程对应的Looper
2.获取对应的MessageQueue
3.拿到msg消息
4.msg消费
public static void loop() {
//1.获取当前线程对应的Looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//2.获取对应的MessageQueue
final MessageQueue queue = me.mQueue;
...
boolean slowDeliveryDetected = false;
for (;;) {
3.拿到msg消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
4.msg消费
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {}
...
msg.recycleUnchecked();
}
}
复制代码
说了这么久马上就到重点了不要着急,现在我们去看看第4步target是什么,
public final class Message implements Parcelable {
//在这里能看到Message是持有Handler的引用,也就是说不存在Handler_A发消息Handler_B会同时接到的问题
@UnsupportedAppUsage
/*package*/ Handler target;
}
复制代码
我们看看target是在哪赋的值
public class Handler {
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {
msg.target = this;
}
}
复制代码
现在我们回到第4步看msg.target.dispatchMessage(msg);是不是就明朗了,现在我们进Handler.dispatchMessage()看做什么处理了
public void dispatchMessage(@NonNull Message msg) {
//在这里,如果msg.callback != null执行handleCallback(),
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
复制代码
//看到这里你悟了吗
private static void handleCallback(Message message) {
message.callback.run();
}
复制代码
总结
postDelay()方法会通过getPostMessage()在消息入队之前封装成一个Message,在消费那里通过判断msg.callback是否为空来判断是否执行Runnable.run()。
Handler五花八门的post/send api们本质上无差别。只是为了让使用者在简单的情况下避免手动封装Message,只需提供一个Runnable即可
只是一些个人的总结,如果有不通的地方欢迎大家交流,错别字也请指出来。