Android 消息机制(二)Handler对消息机制的使用
Android 消息机制(一)消息队列的创建与循环的开始 Looper与MessageQueue中讲述了消息机制的底层实现,下面就从平时所常用的Handler
来讲述消息机制的使用。
Handler
Handler
是我们平时进行异步、多线程开发中常用的一个组件,如果在应用主线程中调用阻塞的或者资源消耗量大的任务,会造成UI的更新卡顿,所以我们会将这样的任务放在新的线程中进行操作。当需要通知UI进行更新时,我们会使用Handler
创建消息丢入主线程的消息队列,再等待主线程的Handler
的处理方法随着消息的处理而被调用,再进行下面的操作。这是Handler
的基本用法,它的实现就与消息机制密切相关。
下面我们就对它的实现进行分析。
构造
Handler
的构造方法中除了实现默认参数的相互调用外,有内容的有这么两个:
1 | public Handler(Callback callback, boolean async) { |
如果传入的了callback
,将会保存到mCallback
域中,之后的消息处理中会看到。
如果没有传入loop
参数,将会使用默认的Looper.myLooper()
也就是之前提到过的本线程TLS
中储存的Looper
对象。mQueue
消息队列就是从该Looper
中获取的消息队列。
1 | public Handler(Looper looper, Callback callback, boolean async) { |
如果传入了looper
,那么我们将从它这里获取对应的消息队列对象,之后的消息就会放入这个队列中,这也是我们可以通过Handler
实现跨线程通信的基础。
发送消息
sendMessage调用链
那么我们直接进入主题:使用Handler
来发送异步处理的消息。
发送消息,我们最常用的是sendMessage()
方法:
1 | public final boolean sendMessage(Message msg) |
1 | public final boolean sendMessageDelayed(Message msg, long delayMillis) |
1 | public boolean sendMessageAtTime(Message msg, long uptimeMillis) { |
最终调用的是sendMessageAtTime()
方法,发送在特定时刻处理的消息。
然后调用enqueueMessage()
方法:
1 | private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { |
这里的第2行中,将msg
的target
设置为this
也就是这个Handler
本身,我们回想起消息循环中处理Message
的调用:
1 | msg.target.dispatchMessage(msg); |
现在我们知道,Handler
发送的消息被消息队列拿到后,会调用发送它的Handler
的dispatchMessage()
方法对它进行处理。
然后,调用了MessageQueue
的enqueueMessage()
方法来向消息队列中插入消息:
enqueueMessage
1 | boolean enqueueMessage(Message msg, long when) { |
整个方法的流程在注释中进行了分析,这里主要就分为了两种情况,需要进行队列唤醒与无需进行队列唤醒的,如果需要队列唤醒操作(有needWake
标记),则会在调用的最后调用nativeWake()
方法进行native
的唤醒操作。
队列的唤醒
1 | static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) { |
native
方法中,利用mPtr
指针找到native
层创建的NativeMessageQueue
对象,然后调用了它的wake()
方法:
1 | void NativeMessageQueue::wake() { |
而接着调用的是NativeMessageQueue
对象的中保存的Native Looper
对象的wake()
方法:
1 | void Looper::wake() { |
这个方法做的事情非常简单,利用write()
函数,向mWakeEventFd
这个fd
中写入了inc
这个值(1)。
为什么只需要这样简单的写入就可以做到唤醒消息队列呢?
我们再回想到上一篇文章中的Native Looper
创建与Epoll
的初始化过程,我们创建了这个eventFd
类型的mWakeEventFd
,并且为它注册了epoll
监听,一旦有来自于mWakeEventFd
的新内容,NativePollOnce()
中的epoll_wait()
调用就会返回,这里就已经起到了唤醒队列的作用。
到这里,发送(插入)新消息到消息队列的过程已经完成,我们只需要等待设置的时间到达,消息队列就会取出我们发送的消息并进行处理。
消息的处理
消息队列拿到消息后,调用msg.target.dispatchMessage(msg);
进行消息的处理,从前文我们了解到,Handler
发送的消息的target
就是Handler
自身,所以调用的就是它的dispatchMessage()
方法:
1 | public void dispatchMessage(Message msg) { |
这个过程也比较经典,第2行if
判断Message
是否拥有自己的callback
,如果有的话就调用handleCallback()
来运行这个Runnable
:
1 | private static void handleCallback(Message message) { |
如果没有自带callback
,第5行检查Handler
是否自带callback
,如果有的话就去执行这个callback
,但是这里还有一点细节需要注意,如果这个方法返回了false
,那么后面Handler
自带的handlerMessage()
方法同样会被执行,这里其实就是一个执行的优先级顺序的问题,一般情况下我们使用时只会传入callback
或是重写Handler
的handleMessage()
方法,优先级也就是确保一个执行顺序的逻辑。
到这里,Handler
的部分就结束了,但是整个消息机制的分析还没有结束,到现在我们分析的都是java
层对消息的处理过程,略过了native
层自己的一套处理来自于native
的消息的机制,下面一篇文章就会把关注点放在这一部分。