在Android整个线程间通信的结构中,Message是通信内容的载体,MessageQueue是Message的管理者,Looper负责从MessageQueue中循环地取消息并分发给对应Handler处理,而Handler是Message的发布者兼处理者。
Handler是实现线程间通信的关键,任何从其他线程发往本线程的消息都要通过Handler发送。Handler在创建时会默认绑定本线程的Looper,这是Handler发出的消息能够被本线程接收的关键。
1.创建Handler
Handler一共有7个构造方法,使用时根据需要指定的参数确定调用哪个构造方法即可。我们先看其中5个只用于简化参数的构造方法:
public Handler() {
this(null, false); //Handler(Callback callback, boolean async)
}
public Handler(Callback callback) {
this(callback, false); //Handler(Callback callback, boolean async)
}
public Handler(Looper looper) {
this(looper, null, false); //Handler(Looper looper, Callback callback, boolean async)
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false); //Handler(Looper looper, Callback callback, boolean async)
}
public Handler(boolean async) {
this(null, async); //Handler(Callback callback, boolean async)
}
&esmp; 这五5个构造方法其实最后都是通过下面两个构造方法完成构造的:
/**
* 将这个标识位设置为true,表示程序需要检测Handler的子类中非静态的 匿名子类、局部子类或者成员子类。
* 这些类可能造成泄露。
* Set this flag to true to detect anonymous, local or member classes
* that extend this Handler class and that are not static. These kind
* of classes can potentially create leaks.
*/
private static final boolean FIND_POTENTIAL_LEAKS = false;
/**
* 构造方法会将Handler和当前线程的{@link Looper}绑定,为Handler设置一个Callback参数以便在处理
* Message时回调Callback参数的{@link android.os.Handler.Callback#handleMessage(Message)},
* 并且根据参数async设定Handler是否需要是异步的。
*
* 默认的Handler都是同步的,除非构造方法中特别指明构造的Handler需要是异步的。
*
* 异步Handler发布的消息都是异步消息。有别于同步消息必须按照顺序处理,异步消息(比如中断消息和事件消息)
* 并不要求处理时是有序的。异步消息不会被{@link MessageQueue#enqueueSyncBarrier(long)}设置的同
* 步障碍器干扰。
*
* @param callback 用于处理消息的Callback接口实现类对象或者null;
* @param async 如果为true,Handler会调用{@link Message#setAsynchronous(boolean)}将接受到
* 的所有{@link Message} 和 {@link Runnable}设置为异步Message。
*
* @hide
*/
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) { //是否检测类性质
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
/**
* 用提供的{@link Looper}对象替代当前线程的Looper与Handler绑定 ,为Handler设置一个Callback参数以
* 便在处理消息时回调Callback参数的{@link android.os.Handler.Callback#handleMessage(Message)},
* 并且根据参数async设定Handler是否需要是异步的。
*
* 默认的Handler都是同步的,除非构造方法中特别指明构造的Handler需要是异步的。
*
* 异步Handler发布的消息都是异步消息。有别于同步消息必须按照顺序处理,异步消息(比如中断消息和事件消息)
* 并不要求处理时是有序的。异步消息不会被{@link MessageQueue#enqueueSyncBarrier(long)}设置的同
* 步障碍器干扰。
*
* @param looper 与Handler绑定的looper,不能为null;
* @param callback 用于处理Message的Callback接口实现类对象或者null;
* @param async 如果为true,Handler会调用{@link Message#setAsynchronous(boolean)}将接受到
* 的所有{@link Message} 和 {@link Runnable}设置为异步Message。
*
* @hide
*/
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper; //不能为null
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
无论是上述哪个构造方法,在构造时都指定了Handler类用于保存Looper对象的成员变量mLooper值。也就是说在创建Handler时,已经为其绑定了Looper对象,这个对象在Handler发送消息时是必不可少的。同样绑定了的还有消息队列mQueue。
Callback类型的mCallback和boolean类型的mAsynchronous都是可选的属性。mCallback参见本文第三节: 消息的分发与处理 Callback接口。mAsynchronous默认是false,用于指明Handler是否是异步Handler。同步Handler和异步Handler的不同在于,异步消息发送消息时会把所有消息都设置成异步消息,同步Handler不管传递过来的消息是同步还是异步都不改动。
2.发送/布消息
&esmp;对外Handler把消息分成了两种:Message和Runnable,发送Message时使用sendMessage()系列方法,发布Runnable时使用post系列方法。在Handler内部Runnable会被封装成简易的消息,之后的操作几乎和Message一致。Runnable的封装如下:
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;
}
sendMessage和post系列方法和Handler的构造方法系列类似,封装了各种参数但是最后真正执行都集中在一两个方法中。我们先来熟悉熟悉sendMessage和post系列方法封装了哪些参数:
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 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);
}
可以看到,所有的post方法都会将Runnable封装成消息并调用对应的sendMessage方法实现入队。源代码非常严谨地为所有方法都加上了final关键词,这使得Handler的子类们不能重写这些方法。
在发送消息时,我们可以根据掌握的资源调用相应的发送方法。需要注意的是执行时间用的并不是日常生活中的时间,而是Android系统自启动开始算起的非深度睡眠时间(称为正常运行时间)。我们发送消息的时候,如果想要指定执行时间就需要调SystemClock.uptimeMillis() 得到当前的正常运行时间,之后再加上需要偏移的延时时间。但不建议这么做,因为提供的API中已经含有丰富的方法能够指定延时时间而非执行时间。
//使消息立即执行
public final boolean postAtFrontOfQueue(Runnable r)
{
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
//发送消息,并立即执行
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);
}
需要特别指出的是postAtFrontOfQueue/sendMessageAtFrontOfQueue方法,前者封装好Runnable后会直接调用后者的。通过这两个方法发送的消息执行时间都会被设置成0,在这个消息进入消息队列时,会被直接放入队列的最前面(如果队列没有其他when=0的消息)。这个方法仅用于非常特殊的情况下。 因为它很容易破坏消息队列的循环,导致顺序问题或者其他不可预见的副作用(援引sendMessageAtFrontOfQueue方法注释)。
/**
* Enqueue a message at the front of the message queue, to be processed on
* the next iteration of the message loop. You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
*
* This method is only for use in very special circumstances – it
* can easily starve the message queue, cause ordering problems, or have
* other unexpected side-effects.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
———— sendMessageAtFrontOfQueue()注释
追踪上述方法的调用关系,可以发现最终都会调用enqueueMessage()。这个方法负责把上层方法封装好的消息加上target和同异步标识,并加入Handler对应的消息队列中。其代码如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在创建Handler时可以指定Handler是同步的还是异步的(保存在mAsynchronous),如果Handler是异步的,在enqueueMessage会将发送的所有消息都设置成异步消息。
queue.enqueueMessage(msg, uptimeMillis)返回的入队结果会一层一层往上反馈。为了保证消息能够被处理,在发送消息后应该考虑发送失败的情况并制定相应的对策。MessageQueue.enqueueMessage()在Android线程间通信(二):MessageQueue(中) 中已经分功能解读过了,在这里就不再累述。当Handler将消息从其他线程入队到对应的消息队列那一刻,也是消息从其他线程移交本线程的一刻。之后消息的分派、处理和大部分管理操作都在本线程中完成。
3.消息的分发与处理 Callback接口
Handler把消息发给消息队列后,消息队列就承担起了消息的管理工作。消息队列在合适的时机会将消息传递给Looper;Looper得到消息后能够从消息中得到其对应的Handler对象,并通过该Handler的dispatchMessage()方法将消息分派给Handler处理;Handler从分派方法中得到消息后,能够根据消息携带的参数指定不同的方法来完成消息的处理。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg); //函数体为:message.callback.run();
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
由上述代码我们可以知道:在分发msg时,callback.run()优先级最高,mCallback.handleMessage(msg)次之,handleMessage(msg)最低。callback.run()是创建callback时需要实现的方法,handleMessage(msg)是创建Handler时需要重写的方法。而mCallback是一个接口,其代码如下:
public interface Callback {
/**
* @param msg {@link android.os.Message Message}对象
* @return 返回true,如果不需要更深层次的处理。返回false则会继续调用{@link #handleMessage(Message)}
*/
public boolean handleMessage(Message msg);
}
Handler的mCallback字段只能在创建Handler时赋值,其效果等同于Handler的handleMessage(msg)。当某个Handler对象发布的消息共享同一个处理程序时,推荐使用Handler对象的handleMessage()方法或者Callback接口来完成消息的处理任务;当一个Handler发布的消息需要不止一种处理程序时,可再结合消息对象中Runnable类型的callback字段。
4.获得Handler对应的Looper
在默认情况下,哪个线程创建Handler,Handler就与哪个线程的Looper对象绑定。除非在创建Handler时,明确地指定了需要绑定的Looper,这种情况一般出现在非UI线程中指定UI线程的Looper对象与Handler对象绑定。
当我们需要得到Handler对应的Looper时,调用Looper.getLooper()方法即可。如果需要主Looper,调用Looper类的静态方法getMainLooper()即可。
常用方法列表:
1.public String getMessageName(Message message):返回消息的名称。如果消息的callback 字段不为空,则返回callback的类名称;为空,则返回what字段的十六进制字符串。
2.public final Message obtainMessage()系列 :调用Message.obtain()系列方法从缓存池取出一个消息。
3. public final void removeCallbacks(Runnable r):移除消息队列中所有callback字段为r的消息。
4. public final void removeCallbacks(Runnable r, Object token):移除消息队列中所有callback总段为r,且obj字段等于token或者null的消息。
5. public final void removeMessages(int what):移除消息队列中所有what字段等于what的消息。
6. public final void removeMessages(int what, Object object):移除消息队列中所有what字段等于what,且obj字段等于object或者null的消息。
7. public final void removeCallbacksAndMessages(Object token):移除所有消息队列中obj等于token或者null的消息。
8. public final boolean hasMessages(int what)
9. public final boolean hasMessages(int what, Object object)
10. public final boolean hasCallbacks(Runnable r)