熟悉Windows编程的朋友可能知道Windows程序是消息驱动的,并且有全局的消息循环系统。而Android应用程序也是消息驱动的,按道理来说也应该提供消息循环机制。实际上谷歌参考了Windows的消息循环机制,也在Android系统中实现了消息循环机制。Android通过Looper、Handler来实现消息循环机制,Android消息循环是针对线程的(每个线程都可以有自己的消息队列和消息循环)。
1、Message的剖析
消息队列的每个元素是Message的一个实例,其中有三个需在实现时定义:
- what 用户定义的int类型消息代码,用来描述消息。
- obj 随消息发送的用户指定对象(所有传递的参数对象)。
- target 处理消息的handler类对象。
Message的目标是Handler类的一个实例。handler可看作是“message handler”的简称。Message在创建时,会自动与一个handler相关联。Message在准备处理状态下,handler是负责让消息处理行为发生的对象。
2、Looper类剖析
Looper类用于封装消息循环,并且有一个消息队列。Looper拥有Message对象的列队,所以Message必须在Looper上发布或读取。
我们通常如下例子一样调用Looper类进行消息循环。
public class LooperThread extends Thread{
public Handler mHandler;
@Override
public void run() {
//1、调用Looper类的prepare函数
Looper.prepare();
........
//2、进入消息循环
Looper.loop();
}
}
//应用程序使用LooperThread
new LooperThread().start();
上面的代码i一共有两个关键调用(1和2两处),我们对其逐一进行分析。 第一个调用函数是Looper的prepare函数,其代码如下:
public static 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定义
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
根据上面的分析知,prepare会在调用线程的局部变量中设置一个Looper对象。这个调用线程就是LooperThread的run线程。prepare函数主要干了一件事:在调用prepare的线程中,设置了一个Looper对象,这个Looper对象就保存在这个调用线程的TLV中。而Looper对象内部封装了一个消息列队。也就是说,prepare函数通过ThreadLocal机制,巧妙地把Looper和调用线程关联在一起了。 第二个函数loop循环函数
public static void loop() {
final Looper me = myLooper();//myLooper返回prepare函数里保存在TLV中的Looper对象。
//取出这个Looper的消息队列
final MessageQueue queue = me.mQueue;
for (;;) {
//取出消息
Message msg = queue.next(); // might block
if(msg != null){
//Message对象中有一个target,它是Handler类型
if (msg.target == null) {//如果target为空,则表示要退出消息循环
return;
}
//调用该消息的Handler,交给它的dispatchMessage函数处理。
msg.target.dispatchMessage(msg);//注意,后面分析Handler类时会讲解到
msg.recycle();
}
}
}
//myLooper函数返回调用线程的线程局部变量,也就是存储在其中的Looper对象
public static Looper myLooper() {
return sThreadLocal.get();
}
通过上面的分析会发现,Looper的作用是:- 封装了一个消息队列。
- Looper的prepare函数把这个Looper和调用prepare的线程(也就是最终的处理线程)绑定在一起了
- 处理线程调用loop函数,处理来自该消息队列的消息。
当事件源向这个Looper发送消息的时候,其实是把消息加到这个Looper的消息队列里了,这么该消息就将由和Looper绑定的处理线程来处理。可事件源又是怎么向Looper消息队列添加消息的呢?这就轮到Handler类登场了。
3、Handler类的分析
Handler中所包括的成员:
final MessageQueue mQueue;//Handler中也有一个消息列队
final Looper mLooper;//也有一个Looper
final Callback mCallback;//有一个回调的类
这几个成员变量是怎么使用的呢?这首先得分析Handler的构造函数。Handler一共有四个构造函数,它们主要的区别是在对上面三个重要成员变量的初始化上。//构造函数1
public Handler() {
//获得调用线程的Looper
mLooper = Looper.myLooper();
//得到Looper的消息队列
mQueue = mLooper.mQueue;
//无callback设置
mCallback = null;
mAsynchronous = false;
}
//构造函数2
public Handler(Looper looper) {
mLooper = looper;//looper由外部传入,是哪个线程的Looper不确定。
mQueue = mLooper.mQueue;
mCallback = null;
mAsynchronous = false;
}
//构造函数3
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;
mCallback = callback;//和构造函数1类似,只不过多了一个设置callback。
mAsynchronous = async;
}
//构造函数4
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
在上述的构造函数中,Handler中的消息队列变量最终都会指向Looper的消息队列。这样做主要是方便往Looper的消息队列里插入消息。假如,在没有Handler的情况下,只能用原始的方法往Looper的消息队列里插入消息:- 调用Looper的getQueue,返回消息队列对象MessageQueue。
- 构造一个Message,填充它的成员,尤其是target成员变量。
- 调用MessageQueue的enqueueMessage,将消息插入消息队列。
这种原始方法的确很玛法,且极容易出错(尤其是target成员变量)。但有了Handler后,我们的工作就 变得简单了。Handler提供了一系列函数帮助我们完成创建消息和插入消息队列,我们只列出其中一部分函数。
//查看消息队列是否有消息码是what的消息
public final boolean hasMessages(int what)
//从Handler中创建一个消息码为what的消息
public final Message obtainMessage(int what)
//从消息队列中移除消息码为what的消息
public final void removeMessages(int what)
//发送一个消息,该消息添加到队尾。
public final boolean sendMessage(Message msg)
//发送一个消息,该消息添加到队列首部,所以优先级很高。
public final boolean sendMessageAtFrontOfQueue(Message msg)
下面看sendMessage函数的实现:public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
return false;
}
msg.target = this;//把Message的target设置为自己,然后加入到消息队列中
return enqueueMessage(queue, msg, uptimeMillis);
}
Handler把Message的target设为自己,是因为Handler除了封装消息添加等功能外,还封装了消息处理的接口。我们往Looper的消息队列中加入了一个消息,按照Looper的处理规则,它在获取消息后会调用target的dispatchMessage函数,再把这个消息派发给Handler处理。Handler是如何处理消息的呢?//android/os/Handler.java
public void dispatchMessage(Message msg) {
//如果Message本身有callback,则直接交给Message的callback处理
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果Handler设置了mCallback,则交给mCallback处理
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//最后才是交给子类处理,子类必须实现handleMessage方法
handleMessage(msg);
}
dispatchMessage定义了一套消息处理的优先级机制,它们分别是:- Message如果自带了callback处理,则交给callback处理。
- Handler如果设置了全局的mCallback,则交给mCallback处理。
- 如果上述都没有,该消息则会被交给Handler子类实现的handlerMessage来处理,当初,这需要从Handler派生并重载HandleMessage函数。
通常情况下,我们一般都是采用第三种方法,即在子类中通过重载handleMessage来完成处理工作的。
4、Looper、Message和Handler的关系
- Looper中有一个Message队列,里面存储的是一个个待处理的Message。Looper类用于封装消息循环。
- Message中有一个Handler,这个Handler是用来处理Message的。
- Handler类封装了消息投递、消息处理等接口。
Activity是一个UI线程,运行于主线程中,Android系统在启动的时候会为Activity创建一个消息队列和消息循环(Looper),在Activity中构造Handler的时候可以指定一个Looper对象,如果不指定则利用当前Activity UI线程的Looper创建Handler对象。这时,我们可以在Activity中可以创建多个工作线程或者其他的组件,如果这些工作线程或者组件把他们的消息通过刚才创建好的Handler对象放入Activity的主线程消息队列,那么该消息就会在Activity UI主线程中处理了。因为主线程一般负责界面的更新操作,并且Android系统中的weget不是线程安全的,所以这种方式可以很好的实现Android界面更新。在Android系统中这种方式有着广泛的运用。