Android Handler消息机制分析
1、基本使用
初始化Handler
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//do something
}
};
在子线程发送消息
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
}).start();
2、原理分析
此处以主线程为例进行分析。其中主要涉及的类有Looper、Handler、MessageQueue、Message
当App启动时,先进入的是ActivityThread类的main()方法。在main()方法中先调用的是Looper.prepareMainLooper();然后最后调用的Looper.loop()。
先看Looper.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();
}
}
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
以上可以看出,prepareMainLooper()方法主要是new了一个Handler(此时初始化了一个MessageQueue),并将这个Handler存放在sThreadLocal中。ThreadLocal可以理解为一个以当前Thread为Key的Map对象,即当前线程关联了一个Looper,儿Looper又持有了一个MessageQueue。
然后看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;
......
//启动一个死循环、获取消息
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) { // No message indicates that the message queue is quitting.
return;
}
......
msg.target.dispatchMessage(msg);
......
}
loop()方法主要是获取当前线程的Looper,循环获取消息,然后dispatchMessage分发消息。
在我们自己用Handler发送消息时,以上可看做是初始化的准备工作。
发送消息:
当我们发送消息时,首先要 new 一个Handler
public Handler(Callback callback, boolean async) {
......
mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看出,Handler有关联一个Looper,此Looper是当前线程的唯一Looper,然后获取到了当前Looper的MessageQueue。此时要发送消息,即可调用sendMessage()方法,sendMessage最后进入sendMessageAtTime(Message msg, long uptimeMillis)方法。
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);}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis);}
获取到MessgeQueue,然后将发送的Message的target的值付为当前的handler,最后进入MessageQueue的enqueueMessage()方法。
boolean enqueueMessage(Message msg, long when){
......
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 {
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;}
在enqueueMessage()方法中,如果只是单纯发送一条消息,mMessages == null, if (p == null || when == 0 || when < p.when) 成立,将msg赋值给mMessages, msg.next = null。然后Looper的loop()方法中,会调用MessageQueue中的next()方法,获取消息,进行分发处理。如果是发送多条延时消息,当第一条消息进入队列,mMessages = msg0,msg0.next = null,当msg1进入时,msg1.when和msg0.when进行比较,如果msg1.when 小于msg0.when,msg1.next = msg0,msg0.next = null,mMessages = msg1;如果msg1.when 大于msg0.when,会进入for循环,一直遍历到最后一个或者当前进入消息的延时小于队列内的消息的延时时,退出循环,将消息插入到退出前的位置。具体可理解为,多条延时消息进入时,队列消息会根据延时的大小,从小到大进行排序。
分发消息:
消息分发由Looper.loop()方法完成,根据Messagequeue.next()方法获取消息。
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}//可能在此阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;Message msg = mMessages;
// msg.target == null 一般不会成立,不会进入
if (msg != null && msg.target == null){
......
}
if (msg != null){
if (now < msg.when){
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
}else{
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
}else{
nextPollTimeoutMillis = -1;
}
......
nextPollTimeoutMillis = 0;}
当第一次进入时,进入for循环,如果没有消息,则mMessages == null ,msg == null,进入到 nextPollTimeoutMillis = -1;在第二次进入循环时,在nativePollOnce()会阻塞,与此对应的,在enqueue()方法中,nativeWake()方法会唤醒阻塞。当有消息时,如果msg的延时大于系统当前时间时,会阻塞到延时时间再唤醒处理消息。当msg的延时小于系统当前时间时,返回msg,将msg.next赋值给mMessages 。然后msg.target调用dispatchMessage分发消息。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
最后进入handleMessage()方法,在创建handler时,重写handleMessage()方法,获取到msg,进行自己操作。