Guava EventBus 的作用
个人对EventBus的理解是: 它是一个事件(消息)发布订阅框架,在我们的应用中可以处理一些异步任务。先通过代码来看下它的简单用法:
package net.leokong.test;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
/**
* Created by leo on 15/10/17.
*/
public class GuavaEventbusTest {
public static void main(String[] args) {
EventBus eventBus = new EventBus();
/**
* 注册事件处理器
*/
eventBus.register(new Object(){
@Subscribe
public void handleUserInfoChangeEvent(UserInfoChangeEvent userInfoChangeEvent){
System.out.println("处理用户信息变化事件:" + userInfoChangeEvent.getUserName());
}
@Subscribe
public void handleUserInfoChangeEvent(BaseEventBusEvent userInfoChangeEvent){
System.out.println("所有事件的父类");
}
});
eventBus.post(new UserInfoChangeEvent("apple"));
}
static class BaseEventBusEvent{
}
static class UserInfoChangeEvent extends BaseEventBusEvent{
private String userName;
public UserInfoChangeEvent(String userName) {
this.userName = userName;
}
public String getUserName() {
return userName;
}
}
}
输出:
处理用户信息变化事件:apple
所有事件的父类
EventBus的基本用法是:
1.先创建EventBus对象
EventBus对象的构造函数如下:
/**
* 创建一个新的EventBus对象,默认名字为"default".
*/
public EventBus() {
this("default");
}
/**
* 使用给定的标识符创建一个EventBus对象,注意该标识符必须是合法的java标识符. */
public EventBus(String identifier) {
this(new LoggingSubscriberExceptionHandler(identifier));
}
/**
* 通过参数指定的 SubscriberExceptionHandler 对象创建EventBus对象
*
* @param subscriberExceptionHandler Handler for subscriber exceptions.
* @since 16.0
*/
public EventBus(SubscriberExceptionHandler subscriberExceptionHandler) {
this.subscriberExceptionHandler = checkNotNull(subscriberExceptionHandler);
}
/**
* 处理subscribers处理事件时抛出的异常
*
* @since 16.0
*/
public interface SubscriberExceptionHandler {
/**
* Handles exceptions thrown by subscribers.
*/
void handleException(Throwable exception, SubscriberExceptionContext context);
}
2.注册事件处理器
/**
* 注册参数object指定的订阅者所有的方法来处理事件
* EventBus通过SubscriberFindingStrategy类的实例来查找订阅者的事件处理方法;
* 默认的策略类是AnnotatedSubscriberFinder
*/
public void register(Object object) {
Multimap<Class<?>, EventSubscriber> methodsInListener =
finder.findAllSubscribers(object);
subscribersByTypeLock.writeLock().lock();
try {
subscribersByType.putAll(methodsInListener);
} finally {
subscribersByTypeLock.writeLock().unlock();
}
}
3.发送事件
/**
* 发送一个事件给所有注册的订阅者. 该方法会在该事件发送给所用的订阅者后成功返回,除非
任何订阅者抛出任何异常。
*如果没有任何注册的订阅者来处理该事件,该事件会被包装为一个DeadEvent来重新发送
*/
public void post(Object event) {
...
}
EventBus的事件对象继承问题
EventBus中的事件可以了是任意类型的,事件分发的时候只需要根据订阅参数类型来分发消息,如果编码中,多个订阅事件类型上存在类型继承的关系,则当前的事件会分发到多个不同的订阅者上,这一点大家在使用的时候可能要仔细处理,在不需要重复处理的消息上就要做好细节处理了。
EventBus的并发问题
Guava EventBus中默认订阅方法为线程不安全的,在异步调度时会自动将其包装成线程安全的方法。对于一般线程安全的实现上,可以通过@AllowConcurrentEvents注解来标识。
/**
* 该注解标识了订阅者的事件处理方法是线程安全的。告诉EventBus该方法可以在多线程环境下同 步调用
* 该注解必须是注解@Subscribe同时使用
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Beta
public @interface AllowConcurrentEvents {
}
EventBus的DeadEvent问题
当EventBus发布了一个事件,但是注册的订阅者中没有找到处理该事件的方法,那么EventBus就会把该事件包装成一个DeadEvent事件来重新发布;我们在应用中可以提供如下的事件处理方法来处理DeadEvent。
@Subscribe
public void onEvent(DeadEvent de) {
logger.info("发布了错误的事件:" + de.getEvent());
}
异步EventBus-AsyncEventBus
上面我们说的都是同步的事件处理,但在我们的应用场景中可能需要异步来处理事件,这时异步EventBus --》 AsyncEventBus 就派上用场了;上代码
/**
* 异步的EventBus
*/
public static void testAsyncEventBus(){
Executor executor = Executors.newFixedThreadPool(10);
AsyncEventBus asyncEventBus = new AsyncEventBus("asyncEventBus", executor);
/**
* 注册事件处理器
*/
asyncEventBus.register(new Object(){
@Subscribe
public void handleUserInfoChangeEvent(UserInfoChangeEvent userInfoChangeEvent){
System.out.println("处理用户信息变化事件:" + userInfoChangeEvent.getUserName());
}
@Subscribe
public void handleUserInfoChangeEvent(BaseEventBusEvent userInfoChangeEvent){
System.out.println("所有事件的父类");
}
});
asyncEventBus.post(new UserInfoChangeEvent("apple"));
System.out.println("异步EventBus");
}
异步EventBus 使用一个Executor来异步执行订阅者处理事件的方法。这样不会因为事件处理代码执行缓慢而导致调用线程阻塞。
转载于:https://blog.51cto.com/leokongwq/1703763