likes
comments
collection
share

“深入交流“系列:Handler消息机制的原理

作者站长头像
站长
· 阅读数 21

Handler消息机制的原理解析

前言

Handler消息机制作为Android系统运行的基础,是开发人员必须要了解的,网上的文章有很多,这里我也简单的叙述一下自己的理解。Android中的异步消息处理机制主要由4个部分组成:HandlerMessageMessageQueueLooper。我们首先来简单的介绍一下这4个组成部分。

Handler

Handler主要的作用就是用来发送消息和处理消息。Android中规定访问UI只能在主线程中进行,如果在子线程中访问UI,就会抛出异常,而Handler就可以将一个任务切换到某个指定的线程中去执行,完美的解决了这个问题。

Message

Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。

MessageQueue

MessageQueue是消息队列,它由单链表构成,内部存储了一组消息并且以队列的方式对外提供插入和删除的工作。MessageQueue作为消息的存储单元,它并不能去处理消息,消息的处理需要依靠Looper进行。消息队列中的消息一般分为三种类型:BarrierMessage(同步屏障)、AsyncMessage(异步消息)、Message(同步消息)。同步屏障消息并不会被执行,只是一个标记。

Looper

Looper用来获取消息队列中的消息,使得在必要的时间让消息得到执行。Looperloop()方法中会开启一个无限循环,用于不断的从MessageQueue中查找消息。如果有消息的话就会取出消息传递给HandlerdispatchMessage()方法中进行处理。如果没有消息就会一直等待。

private Looper(boolean quitAllowed) {
    // 创建Looper的时候会创建一个消息队列
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

一个线程中最多只有一个Looper,一个Looper中只有一个消息队列,一个MessageQueue中可以有多个Message,一个MessageQueue可以对应多个Handler

Handler、MessageQueue、Looper简单汇总

这里借用Carson大佬的一张图来对他们做一个简单的总结:原文

“深入交流“系列:Handler消息机制的原理

Hander流程简述

“深入交流“系列:Handler消息机制的原理

在主线程中创建一个Handler对象,并重写handleMessage()方法,然后当子线程中需要进行UI操作时,就创建一个Message对象,并通过Handler将这条消息发送出去。之后这条消息会被添加到MessageQueue的队列中等待被处理,而Looper中的loop方法会开启一个死循环,尝试从MessageQueue中调用next()方法不断的取出需要处理的消息。最后通过dispatchMessage分发给Handler进行处理。这里需要注意一下,Handler的分发也是有优先级的。

“深入交流“系列:Handler消息机制的原理

  • 1、Message的回调方法:message.callback.run()优先级最高。
  • 2、Handler的回调方法:Handler.mCallback.handleMessage(msg)
  • 3、Handler的默认方法:Handler.handleMessage(msg)

消息入队

“深入交流“系列:Handler消息机制的原理Handler中发送消息的方法有很多,但是无论我们选择了哪个方法来发送消息,它最终都会调用HandlerenqueueMessage()方法来给消息队列MessageQueue发送消息。

“深入交流“系列:Handler消息机制的原理

可以看到在HandlerenqueueMessage()方法中将msg.target赋值为this,这个this也就是我们当前发送消息的这个Handler对象,以为最终消息的处理也会交给这个Handler来处理。紧接着就调用了MessageQueue中的enqueueMessage方法。

“深入交流“系列:Handler消息机制的原理

我们主要分两部分来看:

  • 1、当队头消息为空(p == null)或者消息不需要延迟(when == 0)或者新消息的执行时间小于队头消息的执行时间(when < p.when),就会进入if语句,将新消息插入队列的头部,无论是新插入一条消息还是被消费之后从队列当中移除,mMessage都会指向队头。mBlocked代表当前线程是否处于阻塞状态,如果当前线程处于阻塞状态,那么此时我们插入一条消息到队头就会唤醒这个线程来分发消息。只有当队列中消息为空或者是队列中没有可执行的消息的时候,mBlocked才会为true,处于阻塞状态。

  • 2、进入else语句,会开启一个无限for循环,这个for循环的本质目的就是找到一个合适的位置将新消息插入进去,找到合适的位置之后就会跳出for循环,然后调整链表中节点的指向关系,从而实现新消息插入队列的消息。

消息分发

“深入交流“系列:Handler消息机制的原理

队列中的消息之所以能够分发是因为Looperloop()方法会开启一个无限的for循环,然后调用MessageQueue.next()获取可执行的消息。这个next()方法可能会导致当前线程进入一个阻塞的状态,那么此时这个方法不会有返回值,后续的方法不会执行,直到有可处理的消息返回才会执行。消息处理完之后还会进行回收。接下来进入MessageQueue中的enqueueMessage()方法。

“深入交流“系列:Handler消息机制的原理

可以看到在next()方法中同样开启了一个无限for循环

  • 1natviePollOnce(ptr,nextTimeoutMills):前面提到队列中没有可处理的消息会进入阻塞状态,就是这个方法来完成的,我们主要来看这个方法的第二个参数nextTimeoutMills,如果nextTimeoutMills > 0,当前线程就会进入阻塞状态,并且会释放掉cpu使用权,第一次循环的时候nextTimeoutMills = 0,不会进行休阻塞,如果在后面的过程中没有找到合适的可处理的messagenextTimeoutMills这个值会被更新,第二次循环的时候如果不等于0,就会进行阻塞,假如nextTimeoutMills = 1000,那么线程就会阻塞1000ms1000ms之后继续执行后续代码。如果nextTimeoutMills = -1,那么当前线程会无限的休眠,直到有新的消息进入,唤醒这个线程。
  • 2、判断队头是否为同步屏障消息,msg.target == null即为屏障消息,如果是屏障消息,就会进行do-while循环,while语句中会判断是否为同步消息,如果是同步消息就执行do语句中的代码,如果是异步消息,就退出do-while循环进行后续代码的执行。所以屏障消息的作用就是会从消息队列中获取消息的时候跳过同步消息来检索异步消息。
  • 3、如果msg.target != null,就会按照时间顺序来查找分发消息,这里我们先看else语句,也就是图中的第5步,msg == null的时候进入else语句,表示队头消息为空,也就意味着队列是空的,里面没有消息,然后就会将nextPollTimeoutMills的值置为-1,前面我们提到nextPollTimeoutMills = -1的时候,线程会进入无休止的阻塞,直到新消息到达才会唤醒线程。
  • 4now < msg.when表示找到可以处理的消息了,但是还没有达到此消息执行的时间。此时会重新计算nextPollTimeoutMills的时间。
  • 5、最终找到了需要处理的消息,需要将其从队列中移除,preMsg不为空,表示我们将要删除的消息是队列中间的一条消息,preMsg为空删除的是队头消息。需要重新指向队头消息。最后返回msg对象。

最后取出可处理的消息之后就会调用msg.target.dispatchMessage(msg)方法来分发消息。

同步屏障

“深入交流“系列:Handler消息机制的原理

可以看到在插入消息的过程中,并没有给Messagetarget进行赋值,所以此时这条消息的target = null,也就意味着开启了同步屏障。开启了同步屏障之后,会略过同步消息,异步消息就会优先被处理,那么什么时候会处理同步消息呢,需要调用 removeSyncBarrier将同步屏障移除之后,才会处理同步消息,如果不移除同步屏障,同步消息就不会被处理。