1.概括
Handler对于大家来说并不陌生,它是android消息机制的重要组成成员之一,我们在子线程中更新UI中经常会用到它。因为UI操作是一个频繁且线程不安全的操作,所以更新UI需要在主线程中进行。我们可以在子线程中完成耗时操作,然后再通过Handler发送消息就可以在主线程更新UI了。当然,Handler不仅仅可以更新UI,还具有异步消息处理、线程切换调度等功能。Handler的使用也非常的简单:
首先,我们创建一个Handler对象:
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
textView.setText("hello handler");
break;
}
}
};
这里使用了内部类的方式来定义一个Handler的子类,并重写handleMessage()方法,在该方法中处理收到的消息。
接着我们调用handler的sendMessage()方法来发送一条消息:
Message msg = Message.obtain();
msg.what = 1;
mHandler.sendMessage(msg);
这样,在handleMessage()中就会收到刚才发送的Message,因为what的值等于1所以textView的文本内容会被更新。可以看到,使用Handler来更新UI是非常简单的。那么,这里面的实际流程是怎么样的?稍后我们会通过源码来深入理解。这里先介绍两个类。想必大家都知道,Handler是配合Looper和MessageQueue使用的,那么Looper和MessageQueue是什么?
MessageQueue
MessageQueue的意思是“消息队列”,其作用是存放Handler发送的消息,虽然是叫Queue,但其内部是以单向链表的结构来存放Message的。通过调用enqueueMessage()可以将Message加入到队列当中,然后调用next()方法可取出”对头”的消息。
Looper
Looper的作用是对消息进行轮询操作,Looper是Handler和MessageQueue之间的通讯桥梁。线程默认是没有Looper对象的,需要调用Looper.prepare()
来创建Looper对象,然后通过Looper.loop()
轮询MessageQueue中的Message。
OK,这里先大概了解这两个类的作用是什么就行了,下面附上一张网络图片来帮助大家加深理解。
大致就是这么一个流程,好了,请退后,我要开始撸源码了。
2.源码分析
首先看一下Handler的构造方法,Handler的几个重载的构造方法都会间接调用这个构造方法。创建Handler的时候,需要当前线程的Looper对象,前面提到,线程默认是没有Looper对象的,如果没有Looper对象,创建Handler时就会抛出异常,在第5中行可以看到。
public Handler(Callback callback, boolean async) {
//...省略部分代码
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;
}
mLooper 是Handler的成员变量,在Handler的构造方法中进行初始化,那我们点进去看Looper.myLooper()
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
调用的是sThreadLocal的get()方法,sThreadLocal是ThreadLocal对象,它是Looper类的一个静态成员。声明如下:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
其作用是为每个线程保存一个独立与其他线程的Looper对象,那么我们来看看Looper.prepare()做了哪些工作。
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
在调用Looper.prepare()时会创建一个Looper对象并放入到sThreadLocal中。到这一步就可以知道为什么线程创建Handler先要调用Looper.prepare()了,如果没用调用sThreadLocal.set()存入Looper对象,那么在调用sThreadLocal.get()就会返回null,在初始化Handler的时候就会抛出异常。再来看看Looper的构造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到,MessageQueue对象是在Looper中创建,回到Handler的构造方法,看到第8行的mQueue = mLooper.mQueue。这就说明Handler中的mQueue实际上是Looper对象的成员。这里我们先做一个总结:
1. 创建Handler必须要有该线程的Looper对象,否则会抛出异常
2. 在Looper构造方法中会创建MessageQueue对象
理清了这三者的关系之后,我们看一下Handler的SendMessage(Message msg)方法:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
调用的是sendMessageDelayed(),点击去看:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
点进去看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);
}
sendMessageAtTime()接收两个参数,第一个是Message对象,第二个是处理该Message的时间,事实上,Handler的一系列send()方法最终都会调用sendMessageAtTime()来发送消息。ok,再来看一下enqueueMessage()方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
根据方法名的意思,这一步应该是要将msg加入到MessageQueue队列当中,先在第2行的地方将msg的target设置为this,this就是当前的Hadler对象,这一步我们留到后面分析。最后调用MessageQueue的enqueueMessage()方法,这个才是真正的入队操作,enqueueMessage()就不分析了,主要的工作是将msg插入到链表中,等到Loop.loop()调用后依次取出。那么接下来就看Loop.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;
// 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 (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
这个loop方法还是比较长的,主要看for循环,调用的是MessageQueue的next()方法取出Message,我们看下先next()方法:
Message next() {
//...省略
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//...省略
synchronized (this) {
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;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
//...省略
nextPollTimeoutMillis = 0;
}
}
主要看21行,先判断当前的时间是否小于表头的Message的处理时间,在enqueueMessage()的时候,会根据Message的处理时间来选择插入链表的位置,在表头的Message的处理时间是链表中最小的,所以比较表头的Message即可,如果小于则更新nextPollTimeoutMillis的值,如果大于则说明该Message已到时间处理,那么将其取出交给Looper。
回到Looper.loop(),可以看到,又是一个for死循环,那么这个死循环什么时候会退出呢?看到第15行,当通过MessageQueue的next()方法返回的message为空值时会退出loop循环,那next()方法什么时候返回空值?看一下next()方法的第43行,当MessageQueue的mQuitting变量为true时代表队列结束,这时候就会返回null,那mQuitting什么时候被置为true,我们看一下MessageQueue的结构图可以找到一个quit()方法。
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
在第10行的位置将mQuitting 设置为true,那么又有疑问了,MessageQueue的quit()方法什么时候被调用?我们可以看到Looper中恰好有个quit()方法和一个quitSafely()方法,在这里就会调用MessageQueue的quit()了,这两个方法的区别就不解释了。
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
由此可得,在调用Looper对象的quit()或者quitSafely()即可结束Looper.loop()的死循环。ok,再次回到Looper.loop()方法,看到第36行的 msg.target.dispatchMessage(msg),这里调用msg.target的dispatchMessage()方法。msg.target就是上面所说的Handler,到了这里,我们就知道Handler的enqueueMessage()方法中将msg.target设置成this的作用了。因为设置了target的值,所以在Looper.loop()中就可以知道处理该Message的Handler是哪一个,那么调用这个Handler的dispatchMessage()就可以把这个Message交给它处理。点进去看dispatchMessage()
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
其中,msg.callback就是一个Runnable,可能这个callback我们不太熟悉,但是我们经常会使用Handler的post()方法,先来看一下post方法
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
原来,还是调用了sendMessageDelayed()方法而已,我们点进去看getPostMessage()
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
感觉有点被忽悠了吧?其实我们调用post方法不过是将一个Message的callback设置成你传入的Runnable而已,只不过这个Message不用你手动创建,最终还是调用Handler的send()方法。搞清楚callback是什么后,再回来看dispatchMessage(),还有一个mCallback,这个mCallBack是Handler的内部接口Callback,Handler有一个构造方法可以传入CallBack
public interface Callback {
public boolean handleMessage(Message msg);
}
public Handler(Callback callback) {
this(callback, false);
}
CallBack的作用是可以避免创建Handler的子类再重写handleMessage(),可以看到Callback的handleMessage()返回true就不会执行Handler的handleMessage(),返回false就会往下执行Handler的handleMessage(),这个方法什么都没有做,所以我们需要重写handleMessage()来处理收到的Message。
public void handleMessage(Message msg) {
}
那么到这里为止,整个Handler的消息发送流程就基本说完了,貌似还漏了一个地方,既然Handler的创建是需要所在线程的Looper对象的,Looper对象是通过调用Looper.prepare创建的,然后再通过Looper.loop()进行轮询,但是开头的示例既没有调用Looper.prepare()也没有调用Looper.loop(),为什么不会抛出异常?其实主线程也不例外,也是需要Looper对象才能使用Handler的,所以我们可以看一下主线程的代码,主线程也就是就是main方法执行的线程,main方法在ActivityThread中,我们来看一下ActivityThread这个类:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// 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.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
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");
}
直接看21行,这里调用了Looper.prepareMainLooper(),我们看一下prepareMainLooper()
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
依旧是调用了prepare()方法,没有什么太大的区别,prepare()中传递了一个false表明该Looper不允许被退出,因为它是主线程的Looper,退出了程序就结束了…然后再将sMainLooper初始化。Loop.loop()也在main方法的37行中调用了。好了,以上就是Handler消息机制的大概流程,这里做一个总结:
1. Looper.loop()
方法是一个不断轮询消息的死循环,调用Looper对象的quit()
或者quitSafely()
可结束循环。
2. 主线程默认会创建Looper对象以及调用Looper.loop()
来轮询消息。
我们通过源码的角度来观察会更清晰一些,由于篇幅原因还有很多地方没有说到,Handler的源码还是比较简单的,感兴趣的朋友可以深入去看,第一次写博客,很多地方阐述的不够清晰,见谅。