EventBus 简介
eventbus 是一个Android publish/subscrible 框架,通过解耦发布者和订阅者,简化Android事件传递,从而使代码整洁。
eventbus 3.0和2.0有区别,本文基于3.0描述相关问题
源码分析
- 创建eventbus
EventBus.getDefault().register 或者是 post 等
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
使用了单例模式双重同步锁保证线程安全,效率高
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
//key:订阅的事件,value:订阅这个事件的所有订阅者集合
//private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
subscriptionsByEventType = new HashMap<>();
//key:订阅者对象,value:这个订阅者订阅的事件集合
//private final Map<Object, List<Class<?>>> typesBySubscriber;
typesBySubscriber = new HashMap<>();
//粘性事件 key:粘性事件的class对象, value:事件对象
//private final Map<Class<?>, Object> stickyEvents;
stickyEvents = new ConcurrentHashMap<>();
//事件主线程处理
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
//事件 Background 处理
backgroundPoster = new BackgroundPoster(this);
//事件异步线程处理
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
//订阅者响应函数信息存储和查找类
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
//是否支持事件继承
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
可以看出是通过初始化了一个EventBusBuilder()对象来分别初始化EventBus的一些配置,当我们在写一个需要自定义配置的框架的时候,这种实现方法非常普遍,将配置解耦出去,使我们的代码结构更清晰 这种写法很值得学习
- register 流程
- post 流程
PostThread:默认的 ThreadMode,表示在执行 Post 操作的线程直接调用订阅者的事件响应方法,不论该线程是否为主线程(UI 线程)。当该线程为主线程时,响应方法中不能有耗时操作,否则有卡主线程的风险。适用场景:对于是否在主线程执行无要求,但若 Post 线程为主线程,不能耗时的操作;
MainThread:在主线程中执行响应方法。如果发布线程就是主线程,则直接调用订阅者的事件响应方法,否则通过主线程的 Handler 发送消息在主线程中处理——调用订阅者的事件响应函数。显然,MainThread类的方法也不能有耗时操作,以避免卡主线程。适用场景:必须在主线程执行的操作;
BackgroundThread:在后台线程中执行响应方法。如果发布线程不是主线程,则直接调用订阅者的事件响应函数,否则启动唯一的后台线程去处理。由于后台线程是唯一的,当事件超过一个的时候,它们会被放在队列中依次执行,因此该类响应方法虽然没有PostThread类和MainThread类方法对性能敏感,但最好不要有重度耗时的操作或太频繁的轻度耗时操作,以造成其他操作等待。适用场景:操作轻微耗时且不会过于频繁,即一般的耗时操作都可以放在这里;
Async:不论发布线程是否为主线程,都使用一个空闲线程来处理。和BackgroundThread不同的是,Async类的所有线程是相互独立的,因此不会出现卡线程的问题。适用场景:长耗时操作,例如网络访问。
- unRegister
public synchronized void unregister(Object subscriber) {
//通过typesBySubscriber来取出这个subscriber订阅者订阅的事件类型,
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
//分别解除每个订阅了的事件类型
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
//从typesBySubscriber移除subscriber
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
//subscriptionsByEventType里拿出这个事件类型的订阅者列表.
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
//取消订阅
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
优点:EventBus并不是标准的观察者模式的实现,但是它的整体就是一个发布/订阅框架,也拥有观察者模式的优点,比如:发布者和订阅者的解耦
缺点:EventBus是一个轻量级的东西,这个东西甚至我们自己实现都可以,而广播虽然原理一样,但是实际是个很复杂的东西。两者还有个共同的地方,那就是需要注册与注销,为什么需要注销,因为其一如果你不注销,那么在你不想接收消息的时候,例如你的页面已经关闭了,实际消息还是能接收到,这时候在一个已经关闭了的页面中执行代码是很不安全的;其二在注册时EventBus保存了你订阅者,例如Activity的引用,而你不去注销,那么EventBus就会一直保存着你的引用,这样就引起了内存泄漏。EventBus和Broadcast还有个不同点,那就是Broadcast传递的消息只能是Intent,而Intent中除了序列化的对象外只能传递值,不能传递引用,但是EventBus可以传递任何你想要的东西。我们来想一想EventBus的这种特性的利弊。利,咱们这篇文档一直再说,粗暴点说就是用的爽,不同页面,甚至不同模块之间通讯是如此的省事。而弊,同样是因为太省事了,因为我们忽略了系统设计的初衷。可以想想,为什么Activity之间启动用Intent,之间想要通讯也是用Broadcast传递Intent,而Intent又不能传递复杂的东西,只能是基本类型,即使是序列化的,也是对象的拷贝,原因就在于,系统设计为了使页面与页面之间的耦合降到最低,使他们之间的接触变的最少,自己的逻辑在自己模块中写,而不是通过对象来回传递。所以说从这一点出发,EventBus又打破了这种设计的合理性,一旦使用不合理,会使代码非常的糟糕。例如页面逻辑和业务逻辑可以随便放置,因为只需要通过发送对象就可以让另一个页面另一个模块帮你做处理,如果工程中大量的如此使用,维护者可能就要骂街了。所以使用要合理,呵呵。