赞
踩
逻辑层面的流程前一篇寄快递的举例描述大家肯定都已明了,这里我们从代码层面再串一下流程,并看看主流程中一些核心方法。为了方便阅读将文章分为使用篇和源码解析两篇,上一篇已经写了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对象可以直接new Message() 也可以通过Message.obtain()获取,建议使用后者”。这里解释一下原因哈,关于Message.obtain();方法其实是Message有个静态对象sPool,obtain会判空,如果不空就清理一番(next赋值为null、 .flags赋值为0),为空就new Message() 直接创建一个。
- /**
- * Return a new Message instance from the global pool. Allows us to
- * avoid allocating new objects in many cases.
- */
- public static Message obtain() {
- synchronized (sPoolSync) {
- if (sPool != null) {
- Message m = sPool;
- sPool = m.next;
- m.next = null;
- m.flags = 0; // clear in-use flag
- sPoolSize--;
- return m;
- }
- }
- return new Message();
- }
就像是小时候可以用啤酒瓶换冰棍,为啥呢?因为把旧的返厂清理一下比重新生产节约成本!!!
如果调用obtain传了handler或what或者callback就在获得Message对象返回前最后一步把这些值赋值给targer、what和callback.
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,里面具体实现原理请各位大佬自行研究。
5.简单总结一下:
5.1 handler的sendMessge相关的一些方法用来发消息,post方法其实最后也会调到send方法,然后通过enqueueMessage方法调用MessageQueue的enqueueMessage方法存储消息。
5.2 handler的handleMessage方法或者Handler.CallBack的handleMessage方法,以及post方法的run回调方法。
5.3 消息队列MessageQueue提供了enqueueMessage和next两个方法分别用来取消息和存储消息。前者在5.1中已说明,后者在下面解说。
5.4 Looper 永动机,不是在取消息就是在等待取消息。loop方法负责调用MessageQueue的next方法取出消息,然后使用message的target变量(target就是当初发送这条消息所使用的handler对象)调用handler的dispatchMessage方法根据条件将消息送给 5.2中提到的方法,至此任务已经切到了handler所在的线程执行。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。