Android中的消息机制主要是指Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑。MessageQueue的中文翻译是消息队列,顾名思义,它的内部存储了一组消息,以队列的形式对外提供插入和删除的工作。注意MessageQueue底层其实采用单链表而不是队列;Looper的中文翻译为循环,在这里可以理解为消息循环。由于MessageQueue只是一个消息的存储单元,它并不能去处理消息,而Looper就填补了这个功能,Looper会以无限循环的形式去查找是否有新消息,如果有的话就处理消息,否则就一直等着。
Handler在创建的时候就会采用当前线程的Looper来构造消息循环系统,通过ThreadLocal获取到每个线程Looper。另外线程默认线程是没有Looper的,如果需要使用Handler就必须为线程创建Looper,而我们平时使用Handler有没有创建Looper,因为在UI线程,也就是ActivityThread,ActivityThread在创建的时候就会初始化Looper,这也是在主线程中可以使用Handler的原因。
Handler的主要作用是将一个任务切换到某个制定的线程中去执行,那么Android为什么要提供这个功能呢,这是因为Android规定访问UI只能在UI线程中进行,如果在子线程中访问UI,那么程序就会抛出异常,在ViewRootImpl这个隐藏类中,在android.view包下,在这个类中,有一个方法checkThread方法,如下:
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
这个异常刚开始经常遇到,就是因为在子线程更新UI,就会抛出这个异常。但是又不建议在UI线程进行耗时操作因为会造成程序ANR,所以就Handler机制切换线程更新消息。
那么系统为什么不允许在子线程中访问UI呢?这是因为Android的UI空间不是线程安全,并发访问可能会造成不可预期的状态,那么为什么不加锁呢?首先加上所机制会让UI访问的逻辑变得负责,其次锁机制会降低UI访问的速度,会有等待的线程,就会阻塞一些线程,所以采用了Handler机制。
Handler的使用方法很简单,如果在UI线程创建Handler不需要初始化Looper,原因前面说过,如果在子线程中初始化Handler,需要初始胡Looper,代码如下:
class MyThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// 处理收到的消息
}
};
Looper.loop();
}
}
需要在非UI线程中初始化Looper,先调用Looper.prepare();然后创建Handler。创建完Handler之后,调用Looper.loop进入循环状态。否则就会抛出如下异常
E/AndroidRuntime(27568):can't create handler inside thread that has not called Looper.prepare()
Handler创建完毕后,这个时候其内部的Looper以及MessageQueue就可以和Handler一起协同工作了,先给出一张工作流程图:
如图所示,创建Handler的线程一般为UI线程,如果不是UI线程,需要初始化Looper,这里假设为UI线程,Handler创建完毕之后,就可以和内部的Looper和MessageQueue一起协同工作了,然后通过Handler的post方法或者send方法发送一个消息,这个发送的过程是在其它线程,比如途中的Thread1,Thread2,Thread3,其中post方法最终也会调用send方法,我们表面调用的是sendMesage方法,最终会调用send方法,send方法被调用时,它会调用MessageQueue的enqueueMessage方法将这个消息放入到消息队列中,然后Looper发现有消息到来时,就会处理这个消息,最终Handler中的handlerMessage方法就会被调用,注意Looper,MessageQueue都是运行在创建Handler的线程中的,这样一来,Thread1,Thread2,Thread3中的业务逻辑就切换到创建Handler的线程中去执行了。