Android Handler机制了解多少
Handler的作用
一句话概括,就是将message或者runnable发送到当前线程绑定的MessageQueue当中,通过Looper对象不断的从MessageQueue中取出消息,放到指定的线程中去处理。
主要用途
-
安排message和runnable在将来的某个时刻执行
tip:事实上通过handler发送延迟消息最终都是通过sendMessageDelayed调用到sendMessageAtTime来实现。
-
将要在不同于自己的线程上执行的操作排入队列
-
成员
- message handler接收和处理的消息对象
- MessageQueue 消息队列,单链表结构,先进先出,每个线程只会绑定一个消息队列
- Looper 消息队列的管理者,不断的从消息队列中取出消息,并将消息分发到对应的Handler处理,每个线程只有一个Looper
每个线程可以有多个handler
既然说到Handler机制,那么我们就需要查看一下源码,它内部消息处理的是一个怎么样的流程。
根据上面提到的handler的作用,能够知道消息的排入队列是通过调用MessageQueue对象的enqueueMessage这个方法实现的。我们就来看看这个enqueueMessage到底是怎样实现的。
MeesageQueue.java
方法内先是判断消息的目标handler是否为空,消息是否已经被使用。然后进入到同步块中判断消息队列是否正在退出。紧接着进入到556行的条件判断语句if当中,判断有消息是否已经进入,延时是否为0或者小于之前消息的延时,当有条件成立,则将新msg传给mMessages,并把next指向它,这是msg会在消息队列的最前面,等待loop的轮循。
如果不符合,就进入到else代码中,这里可以看到在567行有一个for的死循环,做了一个when < p.when 的判断,去遍历所有消息有比新消息延时长的消息,再将新的消息通过577,578行的逻辑排到前面去执行。
已经知道了消息是如何排入消息队列中的,那么消息又是如何从消息队列中取出来的呢?通过哪个对象进行操作的呢?接下来就需要分析MessageQueue另一个重要的方法next
MessageQueue.java
nextPollTimeoutMillis = 0 表示nativePollOnece立即返回
nextPollTimeoutMillis > 0 表示nativePollOnece延迟nextPollTimeoutMillis的时间
nextPollTimeoutMillis = -1 表示nativePollOnece进入阻塞状态
直接看320行,能知道在next方法当中也有一个死循环,这个循环虽然代码量很多,但实际操作是很简单。nativePollOnce是一个阻塞方法。在同步块中,首先判断是否msg头部消息是否是屏障消息,如果不是则跳到下一个条件判断 now<msg.when 中若还未到执行时间,就给nextPollTimeoutMillis一个阻塞的时间差,否则进入else中,最后将msg返回出去。倘若msg为空,则nextPollTimeoutMillis置于-1 表示已经没有需要处理的消息。这个时候就进入阻塞状态。
tip:同步块中的第一个if条件判断是判断msg.target是否为空,进而执行。而什么时候msg.target为空呢?就是当MessageQueue.postSyncBarrier 发送同步屏障来保障handler将所有的同步消息不会被执行。同时可以通过Message.setAsynchronous来设置发送的消息为异步消息。
我们知道每个线程都对应一个Looper对象,只是主线程在启动的过程中,其实已经创建了这个Looper。
Looper.java
通过Looper.loop()方法能知道,141行能看到是一个无限循环,执行queue.next()去调用上面提到的消息队列中的next方法取出消息。当队列当中没有消息时,loop方法也会阻塞。
当Looper对象从MessageQueue中取出消息,在164行能看到通过msg.target的目标handler去执行它的dispatchMessage方法将取出的消息分发到指定线程的handleMessage中执行。
tip:handler在发送消息message的时候,通过sendMessageAtTime调用到自身的enqueueMessage方法,就会给当前message的target赋值为当前handler,所以在最后取出消息的时候,只需要通过msg.target就可以获取指定的handler去处理消息。
Handler.java
至此,handler机制就已经写完了,handler机制在我们平时开发过程中经常会使用的到,但是其内部的原理我们又了解多少呢,通过查看源码,能够让我们更清楚的明白内部的处理的过程,加深记忆和更深的使用。