您的位置:首页 > 移动开发 > Android开发

Android EventBus使用与思路总结

2015-10-22 14:12 351 查看
首先说说为什么要用它:

平常开发中免不了要进行各种aci或frgm之间的通信,其实aci之间相对还好,必竟可以通过intent方式,也不会觉得耦合性太强,但frgm之间就麻烦了,除了设置各种接口listener,将frgm做为参数传给另一个frgm外,确实没有其它能满足多种需求的方法。但这种监听器的方式,明显的感觉是这两个frgm是有关系的,代码跟逻辑上很容易混乱,无法成为优质的代码。

我趋向于写一个命令或消息,发出去就当事情完结了,因为对于发命令的这个对象来说,不关注执行过程,甚至于不关注执行结果(如果需要关注结果,就当新起一条线、做新一件事),执行方即使exception了,也影响不到自身。接口监听的方式显然没法做到这么洒脱。 所以有了EventBus,EventBus不能对app的性能产生多大影响,它也不是做这个事的,它存在意义是优化解耦代码、方便开发。

这样就牵扯到两种设计,前一种是监听者模式,后一种是发布、订阅模式。它们的主要区别在于监听者模式由被监听者直接调用订阅者的更新方法,强耦合,而另一种由发布者将消息事件发布给调度中心,由调度中心来分发事件给订阅者,发布者与订阅者之间是没有耦合关系的。

EventBus是greenrobot提供的android发布、订阅模式的实现,该作者另外还写了greendao,如果app中有对数据库大量操作的,需考虑性能问题的,可以去学习使用greendao,应该不会让你失望的。但我现在用的是使用更加方便的AndroidEventBus,它与原生EventBus的区别在于,它查找event监听方法时使用的是注解,而不是固定的字符串方法(打错一个字母都不行,编辑器也没有代码提示,易出错);它支持更精准的消息发送与接收,比如发送一个定位成功的消息,可以只发给对定位信息敏感的页面,而不是通知所有与定位有关的模块(EventBus也勉强支持,但是得写成类似这样,LocationEvent1,LocationEvent2,LocationEvent3····其实这些事件bean内部是完全一致的)。另外补充一句,AndroidEventBus的消息时间在150ms-1000ms之间,大多数维持在200ms左右,个人感觉能稳定在100ms会比较完美,EventBus的效率要高一些,但从使用上说我还是选择AndroidEventBus。

好了,废话一堆,下面开始简要讲解AndroidEventBus的使用与原理。

使用步骤

aci或frgm oncreate时EventBus.getDefault().register(this); ondestory时EventBus.getDefault().unregister(this); this做为一个监听者的角色

编写作为携带数据的消息类,该类无规定,随意编写;编写任意方法名的消息类作为单一参数的方法,加注解@Subscriber(有代码提示,需要精准接收的使用@Subscriber(tag=”xx”))

上面两步是设置消息监听,该步是发消息的地方,调用EventBus.getDefault().post(消息类对象);

如果你在aci与frgm基类中统一register与unregister的方法,提供一个方法给子类来声明使用eventbus,使用上会更简便,我是这样做的

实现原理

为什么在发送消息post时,能将消息精准推送到监听方法处并执行该方法?

首先在register时,遍历监听类的所有方法,找到含Subscriber注解的且只有一个参数的方法,将参数的class与注解设置的tag组合成一个对象EventType,EventType做为一个key值,对应的value是一个含有相应监听者的CopyOnWriteArrayList,保存进map里。

/**
* 查找订阅对象中的所有订阅函数,订阅函数的参数只能有一个.找到订阅函数之后构建Subscription存储到Map中
*
* @param subscriber 订阅对象
* @return
*/
public void findSubcribeMethods(Object subscriber) {
if (mSubcriberMap == null) {
throw new NullPointerException("the mSubcriberMap is null. ");
}
Class<?> clazz = subscriber.getClass();
// 查找类中符合要求的注册方法,直到Object类
while (clazz != null && !isSystemCalss(clazz.getName())) {
final Method[] allMethods = clazz.getDeclaredMethods();
for (int i = 0; i < allMethods.length; i++) {
Method method = allMethods[i];
// 根据注解来解析函数
Subscriber annotation = method.getAnnotation(Subscriber.class);
if (annotation != null) {
// 获取方法参数
Class<?>[] paramsTypeClass = method.getParameterTypes();
// 订阅函数只支持一个参数
if (paramsTypeClass != null && paramsTypeClass.length == 1) {
Class<?> paramType = convertType(paramsTypeClass[0]);
EventType eventType = new EventType(paramType, annotation.tag());
TargetMethod subscribeMethod = new TargetMethod(method, eventType,
annotation.mode());
subscibe(eventType, subscribeMethod, subscriber);
}
}
} // end for
// 获取父类,以继续查找父类中符合要求的方法
clazz = clazz.getSuperclass();
}
}

/**
* 按照EventType存储订阅者列表,这里的EventType就是事件类型,一个事件对应0到多个订阅者.
*
* @param event 事件
* @param method 订阅方法对象
* @param subscriber 订阅者
*/
private void subscibe(EventType event, TargetMethod method, Object subscriber) {
CopyOnWriteArrayList<Subscription> subscriptionLists = mSubcriberMap.get(event);
if (subscriptionLists == null) {
subscriptionLists = new CopyOnWriteArrayList<Subscription>();
}

Subscription newSubscription = new Subscription(subscriber, method);
if (subscriptionLists.contains(newSubscription)) {
return;
}

subscriptionLists.add(newSubscription);
// 将事件类型key和订阅者信息存储到map中
mSubcriberMap.put(event, subscriptionLists);
}


在post时,还是根据EventType找到对应的监听者对象列表,然后invoke(反射执行)监听者的监听方法。

/**
* 处理单个事件
*
* @param eventType
* @param aEvent
*/
private void handleEvent(EventType eventType, Object aEvent) {
List<Subscription> subscriptions = mSubcriberMap.get(eventType);
if (subscriptions == null) {
return;
}

for (Subscription subscription : subscriptions) {
final ThreadMode mode = subscription.threadMode;
EventHandler eventHandler = getEventHandler(mode);
// 处理事件
eventHandler.handleEvent(subscription, aEvent);
}
}


handleEvent即是对监听方法反射执行
subscription.targetMethod.invoke(subscription.subscriber.get(), event);


简单的说,根据消息类的class与注解tag,保存监听者对象的弱引用,在post时,再找到监听者对象,以反射方式执行相应的监听方法(反射解耦)。

再说下消息的运行线程:默认你的消息监听方法运行在UI线程中,你还可以通过注解mode指定其它两种方式

/**
* 事件发布的线程模式枚举
*
* @author mrsimple
*/
public enum ThreadMode {
/**
* 将事件执行在UI线程
*/
MAIN,
/**
* 在发布线程执行
*/
POST,
/**
* 将事件执行在一个子线程中
*/
ASYNC
}


需要关注的东西差不多就上面这些了,细节方面可以阅读AndroidEventBus的源代码,应该都比较容易读懂。

上面这些内容均是个人的总结与记忆,未参考任何文档,并不是一个非常完善的博文,供个人思路梳理与大家简要快速明白该事件库。

附上类图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: