1、概念
Handler可以通过与线程相关的MessageQueue发送和处理Message和Runnable对象。通过这种机制,Android可以在相同进程中不同线程间通信。
2、Handler类
在源码中,Handler类中持有对Looper与MessageQueue的引用,如下:
这两个变量在构造方法执行时会赋值,Handler提供了多个构造方法,如下
通过传入不同的Looper及Callback可以构造不同的Handler对象。比较常用的是不传任何参数的第一个构造方法。这些方法最终都会调用如上图中的第四个构造方法,其中主要逻辑如下:
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
构造方法中,首先会获取当前线程的Looper对象,如果为空的话则抛出异常。下面是Looper中myLooper的方法:
从代码中可以看出,Looper获取的是与当前线程关联的Looper对象。而这个对象的设置是在prepare()方法中, 如下
所以在一个新的线程中使用Handler时,如果不调用Looper.prepare()方法会抛出异常。而在主线程中直接new Handler()没有报错,是因为在主线程启动时调用了相关的代码,如下
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
// 创建主线程的looper
Looper.prepareMainLooper();
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// 开启循环,这里面是无限循环,基本不可能走到后的抛异常。
// 因为一个线程执行完成了就会退出,所以需要阻塞住线程。
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
有了Handler对象之后,我们就可以使用Handler来发送消息了。Handler类提供的多种发送消息的方法,如下:
这些方法最终都会调用到sendMessageAtTime,其代码如下:
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);
}
从代码中可以看到主要的逻辑是调用了enqueueMessage方法,即将消息加入队列。enqueueMessage的方法如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 指定msg的target对象,即为当前Handler
msg.target = this;
// 设置消息是否同步,一般的消息都是同步的。源码中有一些用到了异步的消息。
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 此处的queue是MessageQueue对象,通过Looper对象获取。
return queue.enqueueMessage(msg, uptimeMillis);
}
从代码中可以看出,Handler的sendMessage只是将message加入到了MessageQueue的队列中。 至此Handler完成了消息的发送。
3、MessageQueue
上节中Handler发送消息后实际上是调用了MessageQueue的enqueueMessage方法,代码如下:
boolean enqueueMessage(Message msg, long when) {
// message的target类型为Handler。Handler中调用enqueueMessage方法时已经指定了该message的target对象了。
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
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; // mMessages相当于链表的头节点。Message相当于链表中的节点,持有下一个节点的引用next。
boolean needWake;
// 如果当前节点为空 或 msg的触发时间为0 或 触发的时间早于头节点,则将该msg插入到头节点,并赋值为新的头节点
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg; // 将msg设置为新的头节点
needWake = mBlocked; // 根据block状态来确定是否需要唤醒
} 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.
// 如果消息是异步的 或 target为空的情况需要wake
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;
}
}
// 此时p是null,相当于将p插入到链表尾部。
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
// 调用native方法唤醒(因为looper对象在for循环中调用next方法取消息,而取消息的方法会阻塞线程)
nativeWake(mPtr);
}
}
return true;
}
该方法相当于将消息放到了消息链表的尾部,同时根据消息是否是同步还是异步来唤醒线程。
至此,已经将消息放到链表这样的数据结构了,接下来肯定是要取出这些消息,然后进行相应的操作的。 消息的取出是通过Looper调用了MessageQueue的next方法实现的,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();
}
// 根据nextPollTimeoutMillis的值不同,这里可能会发生阻塞
// -1时会无限阻塞,直到被唤醒。0表示不阻塞,直接返回。其他正数,则会阻塞相应的时间,然后返回。
// 所以刚进来没有消息时,会一直阻塞在这里。
// 在handler发送消息后,MessageQueue的enqueueMessage方法中就会唤醒这里的线程,跳过这里,从而取出消息处理。
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;
// target为空的情况一般在源码中。
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
// 当链表头message的target为空的情况下,找到第一个异步消息。故这之间的同步消息会被忽略掉。
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
// 如果消息的处理时间还未到,则设置nextPollTimeoutMillis
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; // 将prevMsg与msg后面的节点链接起来。
} 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;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
通过该方法,就可以取出一个消息了。拿到消息后,Looper中就会做相应处理。
3.1、MessageQueue的native层
在MessageQueue的源码中使用如下的native代码:
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
在native层与之对应的类是NativeMessageQueue。通过对源码的查看,可以得知MessageQueue的阻塞与唤醒是通过epoll机制实现的。(这里还需要深入了解)
4、Looper
Looper类的loop会调用MessageQueue类的next方法取出消息,loop方法主要功能简化一下如下:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// ...... 省略
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// ...... 省略
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
// 最主要的是这行代码,调用target的dispatchMessage方法。
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
// ...... 省略
msg.recycleUnchecked(); // 回收消息
}
}
从代码中看,最重要的是msg调用了target对象的dispatchMessage方法,而这个target即是Handler类型,也即是在sendMessage时赋值的。由此可以知道,消息的发送和处理均是在Handler中处理的。Handler中dispatchMessage方法如下
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
// 如果消息中携带有callback参数,callback类型为Runnable
if (msg.callback != null) {
handleCallback(msg); // 这个方法会直接调用message.callback.run()
} else {
// 如果在构造Handler时传入了Callback参数,则用此Callback对象处理消息
if (mCallback != null) {
// 如果callback的handleMessage方法返回false,还会继续用handler自身的方法。
if (mCallback.handleMessage(msg)) {
return;
}
}
// 常见的构造handler时需要覆写的方法,handler中该方法为空实现。
handleMessage(msg);
}
}
到这里,即完成了消息的分发与处理。
5、Message
Message类相当于链表中的一个节点,因为其保存了对下一个节点的引用,即next变量。同时还定义了what,arg1,arg2、data,callback成员变量。获取Message的方法除了直接new之外,系统还提供了一个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指向链表的头节点
sPool = m.next;
m.next = null; // 断开与头节点的链接
m.flags = 0; // clear in-use flag
sPoolSize--; // 缓存池大小减1
return m;
}
}
// 如果没有缓存则new一个Message
return new Message();
}
而缓存池的释放则是在message的recycleUnchecked方法中,如下
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
// 采用头插法,将回收到的Message放在链表的头部
// 当前节点的next链接到原链表头部。
next = sPool;
// 将当前节点作为新头节点
sPool = this;
// 缓存池大小加1
sPoolSize++;
}
}
}
6、IdleHandler
在MessageQueue的next方法中,如果消息为空的情况下,会调用IdleHandler对象的queueIdle方法,而Idlehandler对象可以通过MessageQueue暴露的接口添加多个。使用方法如下:
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
Log.i("test", "idleHandler queueIdle " + System.currentTimeMillis());
// 返回true时,会保持该IdleHandler一直活跃。false则会移除该IdleHandler。
return true;
}
});
该方法的注释说明了调用的场景
/**
* Called when the message queue has run out of messages and will now
* wait for more. Return true to keep your idle handler active, false
* to have it removed. This may be called if there are still messages
* pending in the queue, but they are all scheduled to be dispatched
* after the current time.
*/
我们可以通过此方法将一些操作在没有消息时处理,从而避免抢占资源优化交互体验。
7、总结
1、构造Handler时,会通过ThreadLocal获取Looper对象及其MessageQueue对象。Handler发送消息时会调用MessageQueue的enqueueMessage方法将消息加入到链表中去。同时,Looper的loop循环会调用MessageQueue的next方法取出一个消息,调用该消息的dispatchMessage方法,从而将消息分发出去。
2、其中,MessageQueue的next方法中的nativePollOnce会阻塞住线程,而enqueueMessage中会调用nativeWake唤醒线程。
3、主线程中的Looper会阻塞住主线程。由于线程的特性,线程执行完成后就会退出,故ActivityThread类中的Loop.loop方法的无限循环会阻塞住主线程,从而无法走到最后一行的抛出异常。在主线程阻塞没有引起ANR的原因是发生阻塞时没有事件发生,而当有事件发生时,系统会唤醒线程从而避免了ANR。即有事件时唤醒线程处理,无事件时阻塞等待。
4、在一个handler对象上removeMessage时需要注意的是,只能该handler对象的messgae。当出现message不能删除的时候,要检查一下该message是不是关联到当前的handler对象。
5、进程阻塞时,由于操作系统没有分配执行片段给该进程,所以该进程一直在等待状态,不会占用相关资源。
8、参考
以下三篇文章写的非常好。
1、MessageQueue
2、Android中线程间通信机制Handler疑问解答
3、进程阻塞为什么不会占用cpu资源