最近经常使用Handler相关的一些东西。翻了下源码,总结一遍方便回顾。
Android消息机制就是Android的handler机制。
首先是为什么要使用handler?
子线程不能操作ui,因此需要将操作ui的消息传递到主线程,使主线程按照需求更新ui,避免操作不安全。
Handler的重要组成部分:Handler Looper MessageQueue ,其中Looper是Handler和messagequeue通信的桥梁。
Handler是消息处理器。MessageQueue是消息队列;Looper是消息循环器。
重要的方法:
Looper.prepare():为当前线程创建一个looper。在该方法内部,使用ThreadLocal进行实现。把new Looper()保存到ThreadLocal。在Looper的构造方法中创建了消息队列。并获取了对应的Thread。
Looper.loop();开启消息循环,在ActivityThread中,自动开启消息循环。
Looper.myLooper() :获取当前线程的looper
looper.mQueue:looper对应的消息队列。
在handler的构造方法中,先获取looper和messagequeue.实现了handler和looper和message的关联。如下:
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
消息机制的理解可以分为以下三个部分
1、消息的插入
2、消息的循环
3、消息的处理
1 消息分发和入队
在了解消息消息机制前,先了解下消息获取。通常建议使用obtain进行消息的获取,而不是直接new Message,原始是使用obtain方法可以实现消息复用,里边使用了消息池。代码如下:
/**
* 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();
}
接下来进入正题。我们通常使用sendMessage方法进行handler的消息发送(其他的如sendEmptyMessage、post等,大同小异,只是会对信息进行包装,最终调用的方法都是一样的)。这里先以sendMessage为例,后边顺带讲一下post的操作。
话不多说,上源码,注意里边添加的注释
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//在这里进行一个消息的入队操作
return enqueueMessage(queue, msg, uptimeMillis);
}
从这里可以看到消息发送最终就是调用了一个消息入队的方法,把message放到消息队列mQueue中,这个消息队列使用looper.mQueue取出,looper是Handler对应的消息循环器,它创建时会创建一个对应的消息队列。是不是很简单,发送消息就是让消息入队。那怎么入队呢?继续走。。。
boolean enqueueMessage(Message msg, long when) {
....此处代码省略
synchronized (this) {
...
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循环吧消息插入到消息队列中
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;
}
省略了方法中我认为不重要的代码,主要关注重点内容。相关的注释已经在代码中添加,可以看到,消息入队很简单,主要是通过时间比对,按照消息需要执行的时间点插入到队列的相应位置。里边️一个需要关注的点,代码中出现了一个方法nativeWake,这个方法通过native调用唤起被阻塞的方法,代码中的mBlocked表示消息循环是否处于阻塞状态。在if分支中,消息插入到队首,此时如果是被阻塞的状态, 则标记needWake为mBloacked(true)。如果是else分支,插入到队列中间,需要使用needWake和p是否是异步决定是否标记needWake为true。如果前边有消息是异步消息,那本次插入则不需要进行唤醒操作,因为前边异步消息插入的时候已经进行了标记。最后根据needWake的值来确认要不要调用nativeWake方法,该方法的作用是进行唤醒操作,唤醒被阻塞的消息获取过程。
插入队列的消息谁来处理呢?答案是Looper,消息循环器。从队列中去消息进行处理,可以想像成一个传送带,MessageQueue就是传送带,handler往上边放消息,Looper取下来处理。Looper通过loop方法进行消息获取,核心源码如下:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
...
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
//next方法是获取消息的关键方法,是一个阻塞方法
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
}
在loop方法中,实现主要功能的代码块就是一个无限for循环操作,用于不停地从队列中获取消息。获取消息是通过MessageQueue的next方法实现的。该方法是一个阻塞方法,且该方法获取的message正常不会为null,如果为null,loop方法return。next方法的核心源码如下。
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//该方法的作用是通过natvie调用进行阻塞,如果nextPollTimeoutMillis==1,则进行阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
//这个操作是为了保证异步消息的优先处理,使用到了一个消息的同步屏障机制
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//获取到异步消息进行处理
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
...代码省略
}
}
next方法稍微有点长,主要看其中的注释部分。这里有两个关键点,nextPollTimeoutMillis变量和nativePollOnce方法,nextPollTimeoutMillis变量是一个时间标记量,nativePollOnce的形参,该方法中,如果nextPollTimeoutMillis如果值为-1,会进行阻塞,如果不是-1,正常往下走。真正取消息的地方是synchronized代码块,在这里,首先通过一个while循环找到需要处理的异步消息。这里涉及到了同步屏障机制,简单理解,就是为了保证异步消息优先处理,会插入一个同步屏障,同步屏障消息的特点是targe为null,这样的话,在while循环中就会不停往后寻找,直到找到一个异步消息,拿出来。接下来有两种情况:
1、消息拿到了,需要对比一下当前时间和消息中的时间信息,如果当前时间还没有达到消息要执行的时间点,直接使用时间差赋值给变量nextPollTimeoutMillis。直到时间满足了消息执行的要求。在else分支中,从队列中取出消息。返回给Looper。
2、拿到的消息是null,说明队列中没有异步消息,这时候标记nextPollTimeoutMillis为-1,在下一次循环中通过nativePollOnce方法进行阻塞,阻塞过程中不消耗cpu资源。直到有消息入队,进行阻塞唤醒的操作,还记得前边的方法吗?在MessageQueue的enqueueMessage方法中,消息入队后有个nativeWake方法,便是和这个阻塞方法相对应。即,没有消息时使用nativePollOnce阻塞,消息入队后使用nativeWake唤醒。
再次回到Looper的loop方法,在该方法中,如果取到消息,会调用msg.target.despatchMessage方法进行消息的处理。走到这里基本已经接近消息处理的尾声,这个方法中执行的就是消息对应的真正逻辑。
public void dispatchMessage(Message msg) {
//callback是通过post方法传过来的runnable,handleCallback方法中其实就是执行runnable的run方法。
if (msg.callback != null) {
handleCallback(msg);
} else {
//这个mCallback其实就是构造handler的时候作为参数传进来的handler
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//这里是调用构建handler时实现的handleMessage方法。
handleMessage(msg);
}
}
通过上边的代码可以看到despatchMessage方法的执行主要分成三块:
1、执行调用message的callback执行逻辑;
2、调用mCallback执行处理逻辑;
3、调用handleMessage进行逻辑处理。
分别看下
(1)message的callback其实就是我们调用handler的post和postDelay方法中携带的runnable。代码如下:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
通过代码可以看到,使用post进行消息分发,也是把runnable包装成了一个message进行分发和处理,包装完后执行的也是sendMessage操作,后续流程和sendMessage流程一模一样。postDelay同样。这里不尽兴赘述。
(2)mCallback又是个什么东西呢?
这个是handler构造时带过来的,举个栗子:
private Handler testHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
构造参数中的callback就是对应的Handler中的mCallback。因此,如果我们不是使用post方法发送的事件,会优先判断该回调是否为空。
(3)如果上述两种情况都为空了,就会执行我们创建Handler时实现的handleMessage方法,这个方法是不是很熟悉。
总结一下,Android消息机制就是一个线程,使用指定线程的Handler把消息扔到消息队列,指定线程的Looper取出消息在对应线程进行处理。