深入理解Android消息机制之Handler,Looper,MessageQueue

2017-01-14 10:04:55来源:http://www.jianshu.com/p/538dd3e4de2a作者:Allen_Lin人点击

第七城市
前言

重新研究了Android Handler,Looper,MessageQueue的源码,收获很多,这里记录一下。文中会提几个问题,来更好的引起思考。


正文

说到用来线程间通信的Handler机制,我们就会谈及Looper和MessageQueue。简单来说,Looper就是一个循环器,每一个线程都只有自己的一个Looper对象,而每一个Looper对象也都会绑定一个消息队列,Looper的任务就是负责循环读取这个消息队列。如果有消息就交给Handler处理,然后Handler调用handleMessage回调到主线程去更新UI,而如果队列为空则阻塞等待消息返回。而且,Handler的任务除了刚才说的处理消息更新UI,还负责在其他线程中发送消息到消息队列中。下面一张引用他人的图:



图解

注意左上角的Handler持有的引用是主线程的,是主线程把Handler对象的引用传递给其他线程的。


以上的东西相信都能理解,下面提几个问题。
1、如何做到每个线程只有一个looper对象?
这里要引出ThreadLocal的概念了。Looper中有一个成员变量sThreadLocal。


 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

ThreadLocal不是线程,它是一个关于创建线程局部变量的泛型类,使得各线程能够保持各自独立的一个对象,而其他线程访问不到。具体的做法是通过在初始化Looper的时候调用sThreadLocal.set(new Looper())。set方法如下


public void set(T value) {  
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}

即获取当前的线程的ID,为线程实例化一个Looper并存储到这个线程。所以,每一个线程有维持着一个values(里面是一个table数组),放着不同于其他线程的一个Looper,通过ThreadLocal的set和get方法可以访问(Handler的实例化其实内部是调用了sThreadLocal.get()去获取此线程的Looper,然后通过send方法就可以往这个Looper所绑定的消息队列中放消息了)。不同的线程中虽然访问的是同一个ThreadLocal的set和get方法,但它们对ThreadLocal所做的读写操作都只是在各自线程的内部。这样就做到了一个线程拥有唯一一个Looper。


另外,非UI线程是默认没有Looper的,如果需要使用Handler就必须为线程创建Looper,并显式调用prepare和loop方法。而UI线程,其实就是ActivityThread,ActivityThread被创建时就会初始化Looper,这也是在主线程中默认可以使用Handler的原因。


2、消息队列是以队列的数据结构存储消息?
其实消息队列的内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息的,Message对象内部包含一个next变量,该变量指向下一个消息对象。
原因是队列的方式是先进先出,而消息队列中消息是按时间排序的。所以使用链表实现才能更快的在任意位置插入消息,复杂度O(1)。


3、消息队列具体怎么放消息,取消息?
放消息:
MessageQueue对象通过调用enqueueMessage()放消息,首先会判断一下,如果新添加的消息的执行时间when是0,或者其执行时间比消息队列头的消息的执行时间还早,就把消息添加到消息队列头(也就是链表头),否则就找到合适的位置将当前消息添加到消息队列。最终,消息队列中的消息是按when从小到大排序的(when最小的消息在链表头)。


取消息:
取消息是在next()方法中,首先找到消息队列头部的消息或者第一个异步消息,如果当前时间小于此Message的when,就计算时间差并赋值给nextPollTimeoutMillis(到达时间再进行唤醒),否则返回此消息给Looper。
next()是MessageQueue中的一个非常重要的函数,简单来说,它的任务就是获取消息队列中的下一个消息,然后并返回给Looper。下面深入到MessageQueue next方法中:


  Message next() {
//mPtr代表c++层NativeMessageQueue对象
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();
}
nativePollOnce(ptr, nextPollTimeoutMillis);//在这个方法中有可能会发生线程空闲等待,即当nextPollTimeoutMillis=-1时,表示没有消息,或者当nextPollTimeoutMillis>0,即没有到达下一条消息的执行时间。这两种情况下都会调用到C层的epoll_wait函数来等待管道中有内容可读的,直到在enqueueMessage中有新消息入队,此时会调用nativeWake(mPtr)往管道的写入一个字符串从而去唤醒此线程.
synchronized (this) {
// Try to retrieve the next message. Return if found.
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.
//查询MessageQueue中的下一条异步消息
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.
////消息正在退出,返回null
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.
//执行到这里意味着没有消息或者消息的执行时间还没到达。在进入空闲等待状态前,如果应用程序注册了IdleHandler接口来处理一些事情,那么就会先执行这里IdleHandler,然后再进入等待状态
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.
//执行这些注册了的IdleHanlder
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,因为有可能在执行IdleHandler的时候,已经有新的消息加入到消息队列中去了
nextPollTimeoutMillis = 0;
}
}

什么时候会进入阻塞状态?
有两种情况,一是当消息队列中没有消息时,它会使线程进入等待状态;二是消息队列中有消息,但是消息指定了执行的时间,而现在还没有到这个时间,线程也会进入等待状态。


下面提炼Handler,Looper,MessageQueue的整个设计,可以更好的理解整个消息机制


1、首先是Looper


public class Looper {
/**
* 每个线程都只有一个Looper对象
*/
static ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
//消息队列
MessageQueue mQueue;
Looper(){
mQueue = new MessageQueue();
}
/**
* 实例化一个Looper,并存储到该线程。
*/
public static void prepare(){
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
/**
* 在线程上开启循环
*/
public static void loop(){
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
while (true){
// 获取下一个Message,只有当存进去null的时候才会返回null
// 而当队列为空时,会阻塞直到队列有Message再返回
Message msg = mQueue.next();
//如果取到null,就退出
if (msg == null)return;
//交给Message的目标Handler去处理
msg.target.dispatchMessage(msg);
}
}
/**
* 获取当前线程的Looper
*/
public static Looper myLooper(){ return sThreadLocal.get();}
}

2、接着是消息队列


public class MessageQueue {
/**
* 消息队列,Android源码自己实现了线程安全,这里简单模拟原理
*/
private LinkedBlockingDeque<Message> queue = new LinkedBlockingDeque<>();
private final Object lock = new Object();
/**
* 返回一个message
*/
Message next(){
while (true){
if (queue.peek() !=null){
//如果有Message,直接返回
return queue.poll();
}else {
//如果没有Message,则阻塞,等待加入新Message时唤醒
synchronized (lock){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 向队列加入一个Message,并唤醒前面的wait
* 这个函数可以在任意线程调用
*/
void enqueueMessage(Message message){
synchronized (lock){
queue.push(message);
lock.notify();
}
}
}

3、Message的定义


public class Message {
//属性
public int what;
public int arg1;
public int arg2;
public Object obj;
//可以把Runnable接口封装到消息
Runnable callback;
/**
* 目标Handler
*/
Handler target;
public Message(Runnable callback, int what) {
this.callback = callback;
this.what = what;
}
}

4、最后是Handler


public class Handler {
private Looper looper;
final Callback mCallback;
public interface Callback {
boolean handleMessage(Message msg);
}
public Handler(){
this(null);
}
public Handler(Callback callback) {
looper = Looper.myLooper();
mCallback = callback;
}
public void post(Runnable runnable){
send(new Message(runnable,0));
}
//向Looper的消息队列中发送Message
public void send(Message message){
looper.mQueue.enqueueMessage(message);
}
//在此线程中处理Message
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
//子类需要实现此方法去处理消息
public void handleMessage(Message msg) {
}
}

总结

分析完源码后感觉自己对消息机制的理解更深了一点,或者说印象更深刻了。很多时候遇到不懂的东西从源码入手会有一种豁然开朗的感觉。以上如有误解之处,请指出。


参考

Android应用程序消息处理机制(Looper、Handler)分析




第七城市

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台