为了方便阅读将文章分为使用篇和源码解析两篇,上一篇已经写了Handler是什么、有什么、怎们用,这一片从源码的角度分析完整流程,看看Handler消息机制到底是啥原理。才疏学浅,如有错误,欢迎指正,多谢。
完整流程:首先初始化Handler并new一个CallBack重写handleMessage或者run方法等着用来接收消息。
然后使用handler的sendMessage发送消息。
流程已经明确,涉及文件主要有Handler、Message、MessageQueue、Looper;
1.Handler
1.1 Handler类有个handleMessage方法,是个空方法,等后面子类重写该方法上(,方便我们调用方自己实现并接收参数进行处理。就是我们上一篇中3.1的方式一那个TestHandler中的handleMessage方法接收数据的回调。
1.2 Handler类内部有个接口,Callback,只有一个方法 boolean handleMessage(@NonNull Message msg)和Handler类的handleMessage方法同名; 就是我们方式二接收数据的回调。
1.3 我们怎们能收到消息呢?他其实是依赖Looper的,这里先说接收消息的最后一个流程,一个核心方法Handler的dispatchMessage方法,消息会在dispatchMessage方法被调用时送过来,它会判断msg.callback如果不为空则调用它的run()方法,为啥呢?因为它不是Handler内部的CallBack接口,他就是个Runnable,应用场景是上一篇中的场景五; msg.callback为空则判断Handler内部的CallBack对象是否为空,如果不空mCallback.handleMessage(msg))反之则调用handleMessage(msg),详见dispatchMessage方法源码.
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
2. Message
Message类有个target成员变量就是handler,,后面也是通过这个对象回调给Handler的handleMessage的。
可能会发现我们从来没有对Message的target做过赋值的操作啊,其实是源码将消息添加到仓库的时候都会将当前handler赋值给messege,handler的enqueueMessage方法将消息放入到对列前无条件赋值的不管现在是否已经有值,然后调用MessageQueue的enqueueMessage方法将消息存储:
handler的enqueueMessage方法:
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
3.MessageQueue
插入enqueueMessage方法
消息仓库MessageQueue中的enqueueMessage方法,这里是一个单链表循环赋值。
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
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. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in 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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
还有一个取消息的next方法 : 从mMessages取消息,也是工具方法,由Looper来调用。代码较只截取一部分吧,请自行翻阅源码查看:
4.Looper
此时消息已经被添加到了MessageQueue,我们看一下怎样取出来,此时就要用到Looper类的loop方法,代码较只截取一部分吧,请自行翻阅源码查看:
loop方法取消息有一个死循环调用上面提到的MessageQueue的next方法 - 取到msg为空就return终止不空则通过msg.target这个Handler调用dispatchMessage方法,至此已明了
MessageQueue的next方法也有一个死循环,两层死循环嵌套了;
需要调用Looper.loop()就开启了取消息模式,相同的思考,我从来没有调用过,那消息是怎么发给我的呢?是的,主线程的它同样是是在ActivityThread类的main方法里在调用Looper.prepareMainLooper();之后调用了Looper.loop();
ok穿起来了。
handler死循环不会anr是因为nativePollOnce方法,当没有消息时会阻塞在这个native方法,涉及Linux的pipi/epoll机制,阻塞时主线程会释放CPU资源,进入休眠状态,知道下一个消息到达或者事物发生,会通过pipe管道写入疏浚来唤醒主线程继续工作。主线程休眠和死循环区别:休眠是在内核状态里,主线程被挂起,线程状态转移到休眠;而死循环时主线程死锁在这里一直执行同一块代码无法再相应其他事件。只能确定这里native的方法死循环和咱应用层写的死循环不一样,它不会使程序ANR,里面具体实现原理请各位大佬自行研究。