Android系统提供了 一 个Handler类,用来向 一 个线程的消息队列发送一 个消息并处理消息,Handler类内部有mLooper和mQueue两个成员变量,它们分别指向 一 个Looper对象和 一 个MessageQueue对象。创建的Handler中mLooper和mQueue属于哪个线程,那么,其发送的消息和处理消息也就属于哪个线程的。所以我们可以在一个Thread中同时创建多个Handler,但是Looper实例可以是其他线程的,默认情况下,我们在哪个线程创建Handler,其所关联的Looper对象也就是哪个线程的。比如, 应用程序开发中经常使用如下方式来创建Handler,以及发送一个线程消息。
//创建Handler
Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
//处理消息事件
}
}
//创建一个消息
Message msg=Message.obtain();
msg.object="携带的数据";
//发送消息
mHandler.sendMessage(msg)
下面我们先来看看Handler的构造函数,看看他是如何与Looper对象和 一 个MessageQueue对象关联起来的。
public class Handler {
......
public Handler() {
this(null, false);
}
......
public Handler(Callback callback, boolean async) {
......
mLooper = Looper.myLooper();
......
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
......
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
......
}
我们 一 般使用 Handler 类的默认构造函数来创建一个 Handler 对象, 这时候它的成员变量 mLooper 和mQueue 就会分别指向与当前线程所关联的 一 个 Looper 对象和 一 个 MessageQueue 对象。另外还可以知道默认的Handler对象发送的消息都是同步的,不会有延迟。此外,我们也可以自己调用带Looper参数的构造函数,来自己制定Handler所关联哪个线程的Looper对象,这样我们就可以向哪个线程发送消息了,比如,我们可以在子线程中构造一个主线程的Handler对象,这样就可以向主线程发送消息。
当Handler创建完毕,就可以发送消息了,下面我们来我们来看看,消息是如何创建的,这里我们可以直接使用Message类来创建,也可以通过Handler类中的方法帮助创建。
public class Handler {
......
public final Message obtainMessage()
{
return Message.obtain(this);
}
public final Message obtainMessage(int what)
{
return Message.obtain(this, what);
}
public final Message obtainMessage(int what, Object obj)
{
return Message.obtain(this, what, obj);
}
public final Message obtainMessage(int what, int arg1, int arg2)
{
return Message.obtain(this, what, arg1, arg2);
}
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
{
return Message.obtain(this, what, arg1, arg2, obj);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}
......
}
可以看到,其内部实质是调用Message 对象的对应obtain方法,并会自动关联当前的handler对象为作为target,使用Handler创建消息的好处是省去了我们手动设置
target参数,因为使用Handler发送消息后,该消息的处理者一般也是这个Handler本身。下面看看Message类的主要实现。
public final class Message implements Parcelable {
......
/*package*/ Message next;
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
......
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
......
public static Message obtain(Handler h, int what,
int arg1, int arg2, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
m.obj = obj;
return m;
}
......
/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
*/
public Message() {
}
......
}
Message类是一个链表数据结构,它的obtain静态方法有多种重载,区别就是设置的参数不同,这些参数都是可以单独设置的,所以可以根据需求来使用不同参数的方法。尽管Message的构造器是公开的,但是获取Message对象的最好方法是调用Message的一系列obtain静态方法、或者Handler的一系列obtainMessage实例方法。
因为一个Message被处理完后,消息循环会调用这个消息的recycleUnchecked方法来回收这个对象,也就是将这个对象放在全局sPool对象池中,最多可以回收50个Message对象,这样下次可以直接拿来使用,不用再重新new对象。
Handler有了, Message也创建好了,怎样使用Handler来发送这个消息呢?其实,使用Handler发送消息有两种方式:post方式和sendMessage方式。其中post方式其内部也是通过sendMessage方式,只是它给Message设置了一个callback对象,这个callback就是runnable对象。下面是一些发送消息的重载方法
public class Handler {
......
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean postAtFrontOfQueue(Runnable r)
{
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
......
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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);
}
public final boolean sendMessageAtFrontOfQueue(Message msg) {
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, 0);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
......
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
......
}
以上Handler中提供了很多个发送消息的方法,其中除了sendMessageAtFrontOfQueue()方法之外,其它的发送消息方法最终都会调用到sendMessageAtTime()方法中,在该方法中又会调用enqueueMessage()方法,该方法最终调用的是MessageQueue的enqueueMessage方法将消息入队,等待执行。下面来看看
MessageQueue是如何将这个消息入队的。
public final class MessageQueue {
......
boolean enqueueMessage(Message msg, long when) {
(1)消息的有效性检查
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
......
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
(2)消息被插入队列头部
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 {
(3)消息被插入队列中间
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
(4)判断当前消息是否要唤醒线程
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
......
}
(1)消息的有效性检查:从上面可以看出,Message的target参数必须要设置,也就是处理这个消息的Handler对象必须要设置,否则这个消息将不能入队,消息发送失败。
(2)消息被插入队列头部:
由于 一 个消息队列中的消息是按照它们的处理时间
when
从小到大的顺序来排列的, 因此, 当我们将
一个消息发送到 一 个消息队列时, 需要先根据这个消息的处理时间找到它在目标消息队列的合适位
置, 然后再将它插人到目标消息队列中。
由于保存在目标消息队列头部的消息发生了变化, 因此, 当前线程就
需要将目标线程唤醒, 以便它可以对保存在目标消息队列头部的新消息进行处理。 但是, 如果这时
候目标线程不是正处于睡眠等待状态, 那么当前线程就不需要对它执行唤醒操作。 当前正在处理的
MessageQueue对象的成员变量mBlocked记录了目标线程是否正处于睡眠等待状态。 如果它的值等于
true, 那么就表示目标线程正处于睡眠等待状态, 这时候当前线程就需要将它唤醒。 第26行代码将这
个成员变量的值保存在变量needWake中, 以便当前线程接下来可以决定是否需要将目标线程唤醒。
(3)消息被插入队列中间:
由于当前消息是插入在队列中间,那么保存在目标消息队列头部的消息没有发生变化, 因此, 当前线程
都不需要对目标线程执行唤醒操作, 这时候就会将变量 needWake 的值设置为 false 。但是此时,在一种特殊情况下也需要唤醒线程,那就是当前发送的消息是异步消息,而队列头消息的target为null,也就是说当前线程正在睡眠等待处理的这个消息无效了,此时,该消息前面的消息除头消息之外的所有消息都是同步的,那么就应该唤醒线程来处理后面的消息。
(4)判断当前消息是否要唤醒线程:
将要发送的消息插入到目标消息队列中之后, 第50行的语句就检查变量needWake的值是否等于
true。 如果等于, 那么接下来第51行就会调用MessageQueue类的成员函数nativeWake将目标线程唤醒,nativeWake方法是一个Native方法。