Handler是平时用的最多的线程通信机制,都知道一些原理。但是,对于一些细节还是有些不太清楚,下面就带着下面的那些问题来重新熟悉下源码。
- Handler是怎么保证线程间的通信的?
- Looper的死循环,是怎么保证的线程不卡顿的
- Message的消息复用是怎么做的?
先看下怎么使用handler通信的。
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//处理消息
}
};
private void updateMessage() {
new Thread(new Runnable() {
@Override
public void run() {
Message message = mHandler.obtainMessage();
...
mHandler.sendMessage(message);
}
}).start();
}
}
下面我们就从Handler初始化,sendMessage来看下源码。
1,Handler的初始化
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;
}
...
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static Looper myLooper() {
return sThreadLocal.get();
}
Handler在初始化的时候,做了2件事:
- 1,从ThreadLocal里面获取当前线程的looper(mLooper = Looper.myLooper())
- 2,从Looper里面获取MessageQueue (mQueue = mLooper.mQueue)
我们在初始化Handler的时候,最终会调用这个Handler(Callback callback, boolean async)的构造函数。这里面拿到了Looper对象,通过looper拿到了MessageQueue对象。记住在Handler初始化的线程拿到的Looper,跟MessageQueue(一般都在主线程初始化Handler)
2,handler.sendMessage()
Handler.java
public final boolean sendMessage(Message msg)
sendMessageDelayed(Message msg, long delayMillis)
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//拿到queue
MessageQueue queue = mQueue;
...
//调用enqueueMessage
return enqueueMessage(queue, msg, uptimeMillis);
}
...
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//让Message的target对象指向自己
msg.target = this;
...
//调用MessageQueue的enqueueMessage。这个时间,如果没传就是0
return queue.enqueueMessage(msg, uptimeMillis);
}
这个里面做的事情有:
- 1,拿到looper(构造器,Looper.myLooper())
- 2,初始化MessageQueue(构造器,从Looper获取)
- 3,让Message的target指向自己(Handler)
然后,就调用了MessageQueue的enqueueMessage方法。
3,MessageQueue.enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
if (mQuitting) {
//调用Message的recycle方法
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
//第一次的话,mMessages是null的(就是头指针)
Message p = mMessages;
boolean needWake;
//没有消息,或者这个消息没有延迟,或者延迟小于头指针的话
if (p == null || when == 0 || when < p.when) {
//链表,就把这个新消息置为头指针。
msg.next = p;
mMessages = msg;
//如果队列阻塞了,就把needWake置为true
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.
//如果loop阻塞的话,就唤醒
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
我们可以看到,这个MessageQueue的enqueueMessage方法里面做的事情主要是
- 1,如果消息是没有延迟的,我们就把它添加到头指针
- 2,如果有延迟,我们就按照延迟时间遍历消息队列,把他放到合适的位置。
- 3,如果loop阻塞了,有立即执行的消息,我们就唤醒loop
到这里,我们就看完了handler.sendMessage()做的所有事情。
总结下:
* 首先,初始化Handler,获取Looper和Looper里面的MessageQueue对象。
* 然后,调用MessageQueue的enqueueMessage的方法,把Message放到MessageQueue里。
* 最后,如果消息可立即执行,并且Looper.loop()里面的循环阻塞了,就唤醒。
上面看完了我们自己触发(客户端调用)的代码。下面,我们再分析下系统是怎么获取到Message,并重新交给Handler来处理的。这里我们就需要看程序的入口ActivityThread的main方法是怎么执行了。
1,ActivityThread的main方法
public static void main(String[] args) {
...
//这里
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
//这里,我们找到了
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
在这里我们看到了main方法调用了两个方法:
- 1,Looper.prepareMainLooper();
- 2,Looper.loop();
我们先看看Looper.prepareMainLooper()里面都做了什么
Looper.prepareMainLooper()
public static void prepareMainLooper() {
//这里调用了prepare方法
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//这里获取sMainLooper
sMainLooper = myLooper();
}
}
···
private static void prepare(boolean quitAllowed) {
//在prepare的时候,我们从ThreadLocal获取Looper,如果有的话,就报异常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//这里在当前线程创建了一个looper放到ThreadLocal
sThreadLocal.set(new Looper(quitAllowed));
}
···
public static Looper myLooper() {
//从当前线程获取looper对象
return sThreadLocal.get();
}
这里我们看到prepareMainLooper方法,做的事情有:
- 1,创建了一个Looper(主线程),并放到ThreadLocal里面
- 2,sMainLooper从当前线程里面获取一个Looper(sMainLooper就是主线程looper)
- 3,从prepare方法,我们可以看到每个每个线程只有唯一的一个Looper对象。
这里我们需要知道ThreadLocal的作用。
ThreadLocal是线程局部变量,做线程隔离的,就是为每一个使用该变量的线程都提供一个变量值的副本。每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。这样就保证了* 线程的唯一对应关系,一个线程只有一个Looper *。
下面我们来看看Looper.loop()方法
public static void loop() {
final Looper me = myLooper();
//这里从looper里面获取到MessageQueue
final MessageQueue queue = me.mQueue;
...
for (;;) {
//这里从MessageQueue里面取出消息。官方注释可以阻塞
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
//Message的target(上面说了是Handler),这里是运行在主线程。
msg.target.dispatchMessage(msg);
//上面处理完Message,这里就会调用Message的recycleUnchecked
msg.recycleUnchecked();
}
}
在这里,我们看到调用了handler的dispatchMessage方法。这里面做了有4件事情:
- 1,从Looper里面获取MessageQueue。
- 2,从MessageQueue的next方法里面不断的取出消息(有可能阻塞)
- 3,调用Handler的dispatchMessage()方法来不断的处理消息。
- 4,调用了Message的recycleUnchecked()方法。
* 这里强调下第一点,在上面我们看到了,每个线程的Looper都是唯一的,那么在Looper里面的MessageQueue也就是唯一的 *
这里我们再分别看下MessageQueue.next(),Handler.dispatchMessage(),Message.recycleUnchecked(),三个方法
1.MessageQueue的next()方法
Message next() {
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞线程
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;
//如果Message的头指针不是空,但是,Message的target Handler为null
//就是没有消费这个Message的话,就找下一个
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());
}
//如果找到了Message,并且不会null
if (msg != null) {
//如果当前时间小于Message该处理的时间
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 {
//否则,就把mBlocked阻塞置为false,并返回这个消息
// Got a message.
mBlocked = false;
//链表把这个消息去除,改变头指针
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (false) Log.v("MessageQueue", "Returning message: " + msg);
return msg;
}
} else {
//如果没有找到消息的话
// No more messages.
nextPollTimeoutMillis = -1;
}
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;
}
...
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
// 标0代表不用再阻塞等待,可以继续执行
nextPollTimeoutMillis = 0;
}
...
}
}
我们看了MessageQueue的next()方法。这里面做的事情有:
- 1,如果没有消息,把nextPollTimeoutMillis = -1
- 2,如果有消息,没有到时间,nextPollTimeoutMillis = 时间差
- 3,如果有消息,可以执行,就mBlocked改为false
这个是死循环,所以,当没有Message或者有Message没到时间的话,就会修改nextPollTimeoutMillis的值,然后再次执行。就会执行到nativePollOnce方法,在native层面处理阻塞。
第二个,调用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里面的handleMessage()处理消息中。
第三,我们看下Message的recycleUnchecked()方法
void recycleUnchecked() {
//把使用Message放到对象池(链表)
// 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) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
//先从对象池获取Message如果没有,在实例化一个
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();
}
这里我们可以看到,只是把使用过的Message放到sPool的链表里。在下次实例化的时候,可以通过obtain()方法,再次使用。
到这里,我们整个Handler、Looper、Message、MessageQueue就都分析完了。这里我们梳理下
首先,是启动应用程序做的事情:
- 1,我们启动应用的时候,会在主线程初始化唯一一个Looper,并且调用loop()方法。
- 2,Looper.loop()不断的从MessageQueue.next()里面获取Message,并通过 msg.target(Handler).dispatchMessage()方法,交给Handler来处理。
- 3,如果MessageQueue里面没有消息,或者消息没到到执行的时间,就会在native层阻塞线程。
* 然后,我们发送消息: *
- 1,Handler拿到主线程的looper,并从looper里面拿到MessageQueue。
- 2,给Message设置一个target,指向自己。
- 3,调用MessageQueue的enqueueMessage方法。把消息放到消息队列里面,如果,有需要立即执行的消息,并且Looper.loop()在native层阻塞的话,在native层唤醒loop()
* 现在,我们再来看下,一开始的几个问题 *
1。handler是怎么保证线程间的通信的?
我们知道在应用初始化的时候会在主线程初始化一个Looper(也是唯一一个),并调用loop()方法。
因为Looper在每个线程是唯一的。所以,它内部的MessageQueue也就是唯一的。
loop()方法是在主线程运行的,loop()方法里面会不断的从自身的MessageQueue消息队列来获取并处理消息,所以处理消息的过程是在主线程完成的。
我们一般在主线程初始化Handler,所以,Handler内部获取的Looper跟通过Looper获取的MessageQueue,都是主线程的那一个。
我们只是通过子线程来发送了消息,获取消息跟处理消息的过程,都是在主线程完成的。
2.looper的死循环,是怎么保证的线程不卡顿的?
当没有消息的时候或者消息没有到执行时间时候,会在native处理阻塞,不会造成主线程的卡顿
3.Message的消息复用是怎么做的?
在消息被处理了以后,会调用Message的recycleUnchecked()方法,重置Message的属性,放到对象池里面(链表)。我们通过Message的obtain()方法,会首先从链表中获取,如果没有,才会重新实例化一个。这样就完成了Message的复用。
完整的调用图