@SR1s
2017-02-22T22:54:25.000000Z
字数 2647
阅读 1386
编程范式
Handler
消息队列
Android
Android里的Handler
机制本质上就是消息队列的实现。这套机制的关键成员有三:Handler
、Looper
和MessageQueue
。前两个大家都熟悉,后一个就有点面生了。
当然,有同学可能不服:不是说Handler
机制吗,不提Message
能行?
还别说,在整个Handler
实现机制里,Message
还真真是一个不那么重要的主。怪只怪系统的实现太精妙,以至于暴露出来的只有小弟Message
,真正的大咖MessageQueue
反倒是被忽略了。
这整套逻辑概括起来是这样的:
1. Handler
用来封装了发送、处理Message
处理逻辑
2. Looper
把Message
分发到对应的Handler
上处理
3. 这两者中间靠MessageQueue
关联起来
当我们通过Handler.sendMessage(msg)
发送消息时,首先msg.target
会赋值为这个handler
。然后这个msg
就会被加入MessageQueue
这个队列里。在我们创建Looper
的线程里,Looper
会持续地从MessageQueue
里取Message
出来处理。当msg
被处理的时候,就从msg.target
里得到处理这个消息的Handler
,最后这个msg
就由Handler.handleMessage(msg)
处理了。
发现了吧,整个过程中,最核心的就是这个MessageQueue
,没了它整套机制都玩不动了。整个过程也很好理解,但系统偏偏把MessageQueue
很好的隐藏了起来,以至于Handler
的运作机制成了一个让人很难理解的点。
要说Message
是不重要的,也不对。毕竟是系统暴露出来给我们的接口,对于应用开发者来说,它和Handler
是很好的帮手。用好它能给我们解决一些复杂的问题。
我们来看看,Handler机制有什么特点。
最重要的一点:使用简单。但用好不容易。曾经接手过一个项目,项目里大量使用了Handler
作为跨线程交互的机制。一种类型的消息,在各种不同的地方发送,偏偏在发的过程中,有个步骤出错了,整个业务流程就出了问题。当时对Handler
机制不理解,排查了好长一段时间也没有结果,以至于后面看到Handler
都先头大,自然也很少在项目里使用这个功能。
用好Handler,首先要用对地方。要用对地方,首先要理解它机制和特点。机制和特点前面都已经说过了,它最大特点就是单线程和线程安全。这个特性很适合用来实现状态机。
// StateMachineHandler.java
private static final int MSG_CONNECT = 1;
private static final int MSG_SEND = 2;
private static final int MSG_DISCONNECT = 3;
public class StateMachineHandler extends Handler {
@Override
public void handleMessage(Message msg) {
int type = msg.what;
switch(type) {
case MSG_CONNECT:
// 处理连接事件
break;
case MSG_SEND:
// 处理发送事件
break;
case MSG_DISCONNECT:
// 处理断开连接事件
break;
default:
break;
}
}
}
这样我们就不担心不同线程调用带来的多线程问题了,在实现里都不需要使用锁来同步,因为单线程本来就不存在同步问题。而由于是消息队列,所以我们可以在任意线程发送消息,发送的消息会按发送的先后顺序执行。
但这样就可以了吗?并不是。
这种方式其实对外暴露了整个服务的实现机制:基于Handler的消息队列。上层使用者需要跟我们一样,去了解Handler的机制的是如何运作,才能准确的使用我们提供的服务,然后使用这个Handler来发送对应的消息。这样子,我们的实现就和业务紧紧的耦合起来,倘若以后我们想替换内部实现,不使用Handler实现状态机,或者想完全的把实现换掉,那业务代码也不得不跟着改变。
所以,我们最好能够跟系统一样,把内部实现机制给隐藏起来。怎么做呢?其实很简单:收归、统一对外的接口。
以上面的例子来说,业务需要关心的其实只有三个操作:连接(connect)、发送(send)、断开连接(disconnect)。所以我们对外提供这三个方法,把发送消息的操作隐藏在它们的实现中。细心的同学肯定发现了,在上面的例子里,我们的消息类型设置为private
,外部是无法引用到的,这就是为了实现这个目的。
看下代码:
// StateMachineHandler.java
private static final int MSG_CONNECT = 1;
private static final int MSG_SEND = 2;
private static final int MSG_DISCONNECT = 3;
public class StateMachineHandler extends Handler {
@Override
public void handleMessage(Message msg) { ... }
/**
* 进行连接
*/
public void connect() {
sendMessage(obtainMessage(MSG_CONNECT));
}
/**
* 发送指定内容
*/
public void send(String content) {
sendMessage(obtainMessage(MSG_SEND, content));
}
/**
* 断开连接
*/
public void disconnect() {
sendMessage(obtainMessage(MSG_DISCONNECT));
}
}
这样,我们就完全把内部基于Handler的实现机制给隐藏起来,对外部来说,我们只暴露了外部需要了解的几个接口,使用起来也方便清晰了很多。后续内部实现优化,只要不改动约定的接口,把整个实现都换掉都可以。当然,如果我们一直都有意识的去保证我们接口的优雅的话,也可以很方便的把之前的龌蹉实现,换成基于Handler的机智,而不需要担心对业务的改动太大了。
以上是我使用Handler的一点心得和偏好,如有别的使用方式,请一定要告诉我!