Handler我们经常会用到,一般可以用来处理延时任务,或者进行异步耗时操作的同时更新UI等。
在使用过程中,不知道大家是否和我以前初学的时候一样有这样的疑惑:
一般我们都是在UI线程新建static hander对象,并且实现了它的HandleMessage()方法,然后在其他任何地方通过sendMessage()方法发送的消息,最后都会被handleMessage()所处理,包括子线程里调用sendMessage()也可以。那么从sendMessage()到handleMessage(),这其中是经历了怎样的过程呢?
带着这个问题,我们来找源码进行分析。(看这篇文章之前,最好已经听过,或者了解过Handler,Looper, MessageQueue, Message这几个东西,如果没有,那就得多看看其他资料了。)
一、Handler的构造方法做了什么
首先,我们日常使用时,必须得先创建一个handler对象,那么我们看下它的默认构造方法,这是一定会被调用的:
public Handler() {
this(null, false);
}
它直接调用了另外一个构造方法,看看这个构造方法:
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;
}
这个构造方法里,首先对可能出现的内存泄漏问题进行了警告。什么情况下可能出现内存泄漏呢?上面的判断里表明,如果这个Handler类是匿名类,内部类,或者本地类,则必须是static类型的,否则就会有警告。
(注:为什么必须是static类型呢?因为在Java里,匿名类,内部类,或者本地类如果不是静态的,则会隐式地持有一份对外部类的引用。比如我们在一个activity里新建了handler的匿名内部类,则它会隐式地持有此activity的引用。那么存在这样的隐患:有可能handler发送的message还没来得及处理,activity就被finish掉了。本来activity被finish之后是要回收的,但是因为handler还持有对它的引用,activity对象就无法被回收了,造成内存泄漏。)
好了继续看上面的构造方法,警告完内存泄漏之后,接下来是对Handler的几个成员变量做了赋值。其中mCallback和mAsynchronous都是传入的值,一个为null,一个是flase。
重点是mLooper和mQueue。
mLooper是个Looper类型的成员变量,它通过Looper.myLooper()得到。这个Looper.myLooper()是怎样的实现先不管,这里只要先了解Handler类里包含了一个Looper类型的成员变量和一个MessageQueue类型的成员变量mQueue,并且在构造方法中作了初始化就好。
二、Handler如何发送Message
好了,Handler对象创建完毕,接下来就可以使用了。一般我们都是调用它的sendMessage方法。Handler有多个同类型的方法,但是最终调用都是一个,这里就不细说了。下图表示了它们的调用关系:
最终调用的都是Handler的enqueueMessage方法(其中传入的queue就是handler的mQueue成员变量):
Handler还有个post()方法,传入的是个Runnable对象,其最终也是同样的嵌套调用。
Handler的post()方法的解释点这里
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //给要处理的message绑定了目标
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
上面做了个msg.target = this的操作,把发送的message的target赋值为当前的handler对象,这里后面会用到。
三、MessageQueue是如何处理Message的
继续追MessageQueue的enqueueMessage方法,看它做了什么:
boolean enqueueMessage(Message msg, long when) {
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;
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 (;;) {
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;
}
“enqueue message”这个词,从意思上看,就是把消息放入队列。实际上上面这个方法做的也就是这件事,把handler发送的message按时间顺序插入消息队列,也就是MessageQueue中。
具体是怎么做的呢?MessageQueue类里有个Message类型的成员变量mMessages,表示当前需处理的消息,而每一个Message对象都有一个Message类型的next成员变量,表示它的“下一个消息”,而这些消息的插入又是按时间排序的,因此所有这些消息就构成了一个按时间排序的队列。
经过上面的处理,新消息就插入了消息队列合适的位置。
四、Looper对MessageQueue做了什么处理
然后消息一个个排好顺序了,那怎么处理它们呢?我们知道平时都是Handler的handleMessage()进行处理的,所以搜一下,搜到handler的这个dispatchMessage()方法;
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
从上面还暂时看不出来之前我们分析的消息队列和Handler是怎么联系的,不过可以看到的是消息处理之前是怎么分发的:
1. 如果message对象定义了callback的话,则由callback进行处理。
2. 否则,如果定义handler的时候,传入了callback(这个callback类和message的callback类不是同一个类),则由这个callback调用其handleMessage()方法进行处理。
3. 仅当callback的handleMessage()返回false时,handler的handleMessage()才会得到执行。
好了言归正传,继续追查dispatchMessage()是在哪里被调用的,找到不止一处,但是实际我们想要的是这个:Looper类的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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
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();
}
}
五、ThreadLocal保证了Handler的MessageQueue和Looper的MessageQueue,是同一个对象
在上面方法里,最前面几行,得到了一个MessageQueue 对象:
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;
但是这个MessageQueue对象,和之前hander发送的消息最后插入的那个MessageQueue,是不是同一个呢?这就要看final Looper me = myLooper()这句,拿到的Looper对象是哪来的了,看Looper类的myLooper()源码:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
sThreadLocal是Looper类的一个ThreadLocal类型的成员变量:
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
ThreadLocal在这里的意义是,不同的线程,通过get()得到的ThreadLocal对象是不同的,而同一个线程里拿到的是同一个ThreadLocal对象。
既然有get(),那就应该有set(),继续看:
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的构造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
ThreadLocal的set()是在Looper的prepare()方法里被调用的。
到了这里,再回头梳理下,Handler的构造方法里,就确定了它的mQueue,怎么确定的呢?回头再看下handler的构造方法里这么几行:
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
这里得到mLooper和mQueue的方法,和Looper的loop()方法得到Looper对象和MessageQueue对象的方法,是一模一样的,那么很确定了:
Handler的构造方法里,确定了它的mQueue,并且最后传入了sendMessage()方法,然后由这个mQueue调用其enqueueMessage(),把消息插入队列。
然后Looper类的loop()方法里,再次获取到这个mQueue对象,然后逐个取出其中的消息进行分发,交由消息的callback,handler的callback或者handleMessage()方法进行处理。
六、新线程里如何使用Handler
还有个问题:Looper类的prepare()和loop()是在什么时候被调用的呢?这个就不分析源码了。答案是:
- 如果我们需要在新建的线程里创建handler处理任务,则必须手动调用Looper.prepare()和Looper.loop()方法,而且必须像下面这样,:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();// 步骤1
mHandler = new Handler() {// 步骤2
public void handleMessage(Message msg) {
//消息处理逻辑
}
};
Looper.loop(); // 步骤3
}
}
步骤1,2,3的顺序是不能乱的。为什么呢?上面我们贴的源码已经说明了,Looper对象和MessageQueue对象被创建是在Looper.prepare()里,因此这个必须放前面。而Looper.loop()是个循环执行的过程,如果一直有消息的话就会一直运行,所以要放在后面。
2. 那为什么平时在UI线程创建handler对象的时候,不需要调用Looper.prepare()和Looper.loop()方法呢?
实际上,这两个方法,源码里已经调用了,在ActivityThread的main()方法里,可自行查看。
七、Hander的post()方法的分析:
使用Handler进行任务的处理时,可以用post()方法传入一个Runnable对象进行处理。post()方法源码:
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;
}
可见,传入的Runnable对象,被包装成了一个Message对象,作为这个Message对象的callback。然后还是通过sendMessage的方法发送消息。
所以要注意,如果handler对象是在UI线程创建的,则这个Runnable对象的run()方法里不能做耗时操作,因为run()也会运行在UI线程。
总结:
1. Handler用来发送message和处理message.
2. MessageQueue把handler发送的message按时间顺序排序。
3. Looper不断循环从MessageQueue中取出下一个message,然后交给对应的Handler进行分发和处理。
4. 同一个线程里,可以定义多个Handler,但是只会有一个Looper和一个MessageQueue。
另,创建新的Message对象时,用Message.obtain()方法,比new Message()要好,因为obtain()是从消息池取出被recycle()的消息,如果消息池为空再创建新消息。因此效率会高很多,也节省资源。
另,回答一个常见的问题:
应用主线程中的Looper.loop()一直无限循环为什么不会造成ANR?
答:ANR的产生是messageQueue里的消息没有得到及时处理造成的,而不是无限循环造成的。
主线程Looper会从消息队列循环调用Queue.next()读取消息,当读完所有消息时,主线程会阻塞在Queue.next()方法这里。此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据,主线程才会被唤醒继续工作。
当消息队列有了消息,管道写入数据时,主线程即被唤醒,从管道读取数据。当消息读取完毕,会再次休眠。
因此主线程大多数时候都是处于休眠状态,loop的循环并不会对CPU性能有过多的消耗。