5.4 Looper和Handler类分析
就应用程序而言,Android系统中Java的应用程序和其他系统上相同,都是靠消息驱动来工作的,它们大致的工作原理如下:
有一个消息队列,可以往这个消息队列中投递消息。
有一个消息循环,不断从消息队列中取出消息,然后处理。我们用图5-1来展示这个工作过程:
图 5-1 线程和消息处理的原理图
从图中可以看出:
事件源把待处理的消息加入到消息队列中,一般是加至队列尾,一些优先级高的消息也可以加至队列头。事件源提交的消息可以是按键、触摸屏等物理事件产生的消息,也可以是系统或应用程序本身发出的请求消息。
处理线程不断从消息队列头中取出消息并处理,事件源可以把优先级高的消息放到队列头,这样,优先级高的消息就会首先被处理。
在Android系统中,这些工作主要由Looper和Handler来实现:
Looper类,用于封装消息循环,并且有一个消息队列。
Handler类,有点像辅助类,它封装了消息投递、消息处理等接口。
Looper类是其中的关键。先来看看它是怎么做的。
5.4.1 Looper类分析
我们以Looper使用的一个常见例子来分析这个Looper类。
[—>例子1]
//定义一个LooperThread。
class LooperThread extends Thread{
public Handler mHandler;
public void run(){
//①调用prepare。
Looper.prepare();
……
//②进入消息循环。
Looper.loop();
}
}
//应用程序使用LooperThread。
{
……
new LooperThread().start();//启动新线程,线程函数是run
}
上面的代码一共有两个关键调用(即①和②),我们对其逐一进行分析。
1.准备好了吗
第一个调用函数是Looper的prepare函数。它会做什么工作呢?其代码如下所示:
[—>Looper.java]
public static final void prepare(){
//一个Looper只能调用一次prepare。
if(sThreadLocal.get()!=null){
throw new RuntimeException("Only one Looper may be created per thread");
}
//构造一个Looper对象,设置到调用线程的局部变量中。
sThreadLocal.set(new Looper());
}
//sThreadLocal定义
private static final ThreadLocal sThreadLocal=new ThreadLocal();
ThreadLocal是Java中的线程局部变量类,全名应该是Thread Local Variable。我觉得它的实现和操作系统提供的线程本地存储(TLS)有关系。总之,该类有两个关键函数:
set:设置调用线程的局部变量。
get:获取调用线程的局部变量。
注意 set/get的结果都和调用这个函数的线程有关。ThreadLocal类可参考JDK API文档或Android API文档。
根据上面的分析可知,prepare会在调用线程的局部变量中设置一个Looper对象。这个调用线程就是LooperThread的run线程。先看看Looper对象的构造,其代码如下所示:
[—>Looper.java]
private Looper(){
//构造一个消息队列。
mQueue=new MessageQueue();
mRun=true;
//得到当前线程的Thread对象。
mThread=Thread.currentThread();
}
prepare函数很简单,它主要干了一件事:
在调用prepare的线程中,设置了一个Looper对象,这个Looper对象就保存在这个调用线程的TLV中。而Looper对象内部封装了一个消息队列。
也就是说,prepare函数通过ThreadLocal机制,巧妙地把Looper和调用线程关联在一起了。要了解这样做的目的是什么,需要再看第二个重要函数。
2.Looper循环
代码如下所示:
[—>Looper.java]
public static final void loop(){
Looper me=myLooper();//myLooper返回保存在调用线程TLV中的Looper对象。
//取出这个Looper的消息队列。
MessageQueue queue=me.mQueue;
while(true){
Message msg=queue.next();
//处理消息,Message对象中有一个target,它是Handler类型。
//如果target为空,则表示需要退出消息循环。
if(msg!=null){
if(msg.target==null){
return;
}
//调用该消息的Handler,交给它的dispatchMessage函数处理。
msg.target.dispatchMessage(msg);
msg.recycle();
}
}
}
//myLooper函数返回调用线程的线程局部变量,也就是存储在其中的Looper对象。
public static final Looper myLooper(){
return(Looper)sThreadLocal.get();
}
通过上面的分析会发现,Looper的作用是:
封装了一个消息队列。
Looper的prepare函数把这个Looper和调用prepare的线程(也就是最终的处理线程)绑定在一起了。
处理线程调用loop函数,处理来自该消息队列的消息。
当事件源向这个Looper发送消息的时候,其实是把消息加到这个Looper的消息队列里了。那么,该消息就将由和Looper绑定的处理线程来处理。可事件源又是怎么向Looper消息队列添加消息的呢?来看下一节。
3.Looper、Message和Handler的关系
Looper、Message和Handler之间也存在暧昧关系,不过要比RefBase那三个简单得多,用两句话就可以说清楚:
Looper中有一个Message队列,里面存储的是一个个待处理的Message。
Message中有一个Handler,这个Handler是用来处理Message的。
其中,Handler类封装了很多琐碎的工作。先来认识一下这个Handler。