背景
在Android开发中,经常需要在不同的组件之间通信。函数调用就可以看成是一种通信,调用者和被调用的函数是消息的发送方和接收方,参数和返回值是消息内容。除了直接调用以外,比较常见的就是总线形式的通信。
总线通信有很多种实现,例如EventBus、RxBus、LiveEventBus等,Android原生的Handler也可以作为总线通信组件使用。常用的Observer设计模式也可以理解成专用的总线通信,只能负责收发固定类型的事件。
多数通用总线通信组件都是单向通信,即发送方主动将Message/Event发送给接收方,但有时候会有不同的需求,发送方希望主动获取接收方的数据,此时需要接收方在受到拉取数据的Message之后再回发一个Message。
实现
下面介绍一些基于动态代理实现的总线通信组件,发送方可以直接调用接收方的方法,由于方法可以有参数和返回值,因此可以实现双向通信。
总线通信在代码层面的本质就是一个全局变量,最简单的做法是直接有一个全局的Set保存所有Handler(Handler为接收方),调用方从Set中遍历符合条件的Handler然后逐个调用即可。但是这样的做法调用起来比较繁琐,于是在此基础上借助Java的动态代理做了封装,用起来更方便,效果有点类似jQuery中批量操作DOM元素的写法。关键代码如下。其中Set<Object> mHandlers
中保存所有的Handler,通过register和unregister方法注册/解注册Handler。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| public class EventBus {
private final Set<Object> mHandlers = new HashSet<>();
public static EventBus getDefault() { return Holder.BUS; }
public void register(Object handler) { if (handler != null) { mHandlers.add(handler); } }
public void unregister(Object handler) { if (handler != null) { mHandlers.remove(handler); } }
@NonNull @SuppressWarnings("unchecked") public <Handler> Handler find(Class<Handler> clazz) { final Class<?>[] interfaces = new Class[]{clazz}; final EventInvocationHandler<Handler> handler = new EventInvocationHandler<>(clazz); return (Handler) Proxy.newProxyInstance(clazz.getClassLoader(), interfaces, handler); }
@NonNull @SuppressWarnings("unchecked") public <Handler> IterableList<Handler> findAll(Class<Handler> clazz) { IterableList<Handler> list = new IterableList<>(); for (Object handler : mHandlers) { if (clazz.isInstance(handler)) { list.add((Handler) handler); } } return list; }
private static class Holder { private static final EventBus BUS = new EventBus(); }
private class EventInvocationHandler<Handler> implements InvocationHandler {
private final Class<Handler> mClazz;
EventInvocationHandler(Class<Handler> clazz) { mClazz = clazz; }
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; boolean first = true; for (Object handler : mHandlers) { if (mClazz.isInstance(handler)) { if (first) { result = method.invoke(handler, args); first = false; } else { method.invoke(handler, args); } } } return result; } } }
|
用法
1、定义接口
收发方按照一个定义好的Java接口通信。
1 2 3
| public interface TestHandler { String getName(); }
|
2、接收方的实现
作为接收方的Handler实现这个接口,并注册到EventBus中。
1 2 3 4 5 6 7 8
| TestHandler handler = new TestHandler() { @Override public String getName() { return "0"; } }
EventBus.getDefault().register(handler);
|
3、基本调用方式
当调用方需要调用所有TestHandler时,可以调用EventBus的find方法,该方法返回一个TestHandler类型的动态代理对象,调用这个代理对象的getName方法,则所有TestHandler的相应方法就会被调用。返回值为第一个TestHandler对应方法的返回值。
这个调用过程不需要对find的返回值做判空,也不需要自行遍历每个TestHandler。
1
| EventBus.getDefault().find(TestHandler.class).getName();
|
4、遍历调用方式
对于需要对匹配的元素逐个处理的情况,还提供了findAll方法,该方法返回一个IterableList(继承自ArrayList),可以自行逐个遍历元素,也可以调用each和map进行快捷操作。
1 2 3 4 5 6 7 8 9 10 11 12 13
| bus.findAll(TestHandler.class).each(new EventAction<TestHandler>() { @Override public void run(TestHandler testHandler) { testHandler.getName(); } });
List<String> list = bus.findAll(TestHandler.class).map(new EventMapAction<TestHandler, String>() { @Override public String run(TestHandler handler) { return handler.getName(); } });
|
当然这种总线通信机制也有其不足,例如通信过程对于发送方是阻塞的,不能支持多线程之间的通信等。
完整的代码和测试用例详见GitHub。