一,概述
EventBus即事件总线,在软件架构设计中,采用事件-发布-订阅类架构,通常具备高内聚低耦合特性,相对比通过Android#Broadcast方式,此框架由于不涉及跨进程方式,效率更加高效,并且提供了类似注解订阅方式,EventBus全局访问,不会被局限context组件。
笔者理解,这就是解耦框架,将不关联模块解耦,只通过总线方式通线,在企业级架构设计中,EventBus被经常使用。
二,实例
依赖
implementation "org.greenrobot:eventbus:3.3.1"
获取EventBus实例通常由默认方法getDefault,
或者通过build定制化参数获得,比如可以在build中添加SubscriberInfoIndex接口,自定义订阅方法,这就无需依赖Subscribe注解,
并且重写getSubscriberInfo方法即可
但笔者仍推荐Subscribe注解,但如果一个类中方法特别多,反射多方法存在性能开销,可以考虑index方式快速找到方法,
三,源码
1,register
1,通过subscriberFinder找到订阅方法,核心就在于怎么找,要么通过注解,要么通过index接口
2,对单个实例订阅该方法,订阅事件即方法参数
跟进findSubscribeMethods
1,从缓存查找,命中即返回
2,如果build参数ignoreGeneratedIndex为true,则直接通过反射查找,否则可以尝试index接口
3,index接口超找订阅方法
4,缓存
跟进findUsingInfo方法,
1,通过getSubscriberInfo找到SubscribeInfo,
2,如果没有index接口,则通过反射查找
跟进findUsingReflectionInSingleClass
1,反射所有声明方法
3,找到Subscribe注解方法
4,创建SubscribeMethod实体,保存此方法
最后,根据这个findState返回method
再将FindState放进对象池中,此处是享元模式设计,减少FindState创建数量
2,post
sticky粘性事件,sticky区分状态或事件,事件存在实时性,而状态存在唯一性,因此,状态一般是粘性,而事件则是非粘性,
在register中,通过订阅方法的sticky字段,决定是否将stickEvents中事件发布给订阅者,
stickyEvents需要显示添加,否则普通post仅是普通事件,无法加入stickyEvents Map
直接看post源码
1,从ThreadLocal中获得PostingThredState,其isPosing表示是否正在发送事件
当isPosing为false,则通过eventQueue出队消费事件,
跟进postSingleEvent
上述,存在可继承性事件判断,这在构造EventBus中设置,如下,
跟进postSingleEventForEventType
通过subscriptionEventType通过event快速获得订阅者,
这行map在register的subscribe中添加
跟进postToSubscription发布时间到subscription
根据线程模型,选择策略
POSTING:当前线程
MAIN:当前是主线程直接触发,或者加入主线程队列
MAIN_OROERED:加入主线程队列
BACKGROUND:子线程
ASYNC:反正异步,也是一个子线程
跟进invokeSubscribert
这就很简单了,method#invoke传入event即可,通知到订阅者
四,注意
避免内存泄漏,对于存在生命周期的对象,或需要销毁的对象,及时调用EventBus#unregister方法反注册,不然将作为Subscription#subscriber对象存在EventBus引用中。
五,非反射实现的EventBus
以下,笔者提供一份非反射实现的EventBus,仅供参考,
Event
package eventbus;
public final class Event {
public Class<?> type;
public Object payload;
/**
* @noinspection unchecked
*/
public <T> T castPayload(Class<T> clazz) {
return (T) payload;
}
public Object getPayload() {
return payload;
}
}
EventFilter
package eventbus;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public final class EventFilter {
private final List<Class<?>> eventList = new ArrayList<>();
private final Map<Class<?>, EventInfo> eventInfoMap = new HashMap<>();
public void addEvent(Class<?> event) {
addEvent(event, EventHandleThread.POST);
}
public void addEvent(Class<?> event, EventHandleThread eventHandleThread) {
addEvent(event, eventHandleThread, false);
}
public void addEvent(Class<?> event, EventHandleThread eventHandleThread, boolean sticky) {
addEvent(event, eventHandleThread, sticky, 0);
}
public void addEvent(Class<?> event, EventHandleThread eventHandleThread, boolean sticky, int priority) {
if (!eventList.contains(event)) {
eventList.add(event);
}
EventInfo eventInfo = new EventInfo();
eventInfo.eventHandleThread = eventHandleThread;
eventInfo.sticky = sticky;
eventInfo.priority = priority;
eventInfoMap.put(event, eventInfo);
}
public List<Class<?>> getEventList() {
return eventList;
}
public EventHandleThread getEventHandleThread(Class<?> event) {
EventInfo eventHandleThread = eventInfoMap.get(event);
if (eventHandleThread == null) {
return EventHandleThread.POST;
}
return eventHandleThread.eventHandleThread;
}
public int getEventPriority(Class<?> event) {
EventInfo eventHandleThread = eventInfoMap.get(event);
if (eventHandleThread == null) {
return 0;
}
return eventHandleThread.priority;
}
public boolean isStickyEvent(Class<?> event) {
EventInfo eventHandleThread = eventInfoMap.get(event);
if (eventHandleThread == null) {
return false;
}
return eventHandleThread.sticky;
}
private static class EventInfo {
public int priority;
EventHandleThread eventHandleThread;
boolean sticky;
}
}
EventSubscriber
package eventbus;
@FunctionalInterface
public interface EventSubscriber {
void onReceiveEvent(Event event);
}
EventHandleThread
package eventbus;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public enum EventHandleThread {
POST(Runnable::run),
MAIN(new Executor() {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable command) {
handler.post(command);
}
}),
BACKGROUND(Executors.newCachedThreadPool(new ThreadFactory() {
private final ThreadFactory threadFactory = Executors.defaultThreadFactory();
@Override
public Thread newThread(Runnable r) {
Thread thread = threadFactory.newThread(r);
thread.setDaemon(true);
return thread;
}
})), IO(Executors.newSingleThreadExecutor());
private final Executor executor;
EventHandleThread(Executor executor) {
this.executor = executor;
}
public void submit(Runnable runnable) {
executor.execute(runnable);
}
}
EventBus
package eventbus;
import java.util.ArrayDeque;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public final class EventBus {
private final Map<Class<?>, List<EventSubscriber>> mEventMap = new ConcurrentHashMap<>();
private final Map<Object, EventFilter> mFilterMap = new ConcurrentHashMap<>();
private final Map<Class<?>, Event> mStickyMap = new ConcurrentHashMap<>();
private final Queue<Object> mPostingQueue = new ArrayDeque<>();
private volatile boolean mPosting;
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private static class InstanceHolder {
private final static EventBus INSTANCE = new EventBus();
}
private EventBus() {
}
public static EventBus getInstance() {
return InstanceHolder.INSTANCE;
}
public void register(EventSubscriber obj, EventFilter eventFilter) {
assert obj != null;
assert eventFilter != null;
mFilterMap.put(obj, eventFilter);
for (Class<?> event : eventFilter.getEventList()) {
List<EventSubscriber> objList = mEventMap.computeIfAbsent(event, it -> new CopyOnWriteArrayList<>());
objList.remove(obj);
objList.add(obj);
if (eventFilter.isStickyEvent(event)) {
//do some things
Event stickyEvent = mStickyMap.get(event);
if (stickyEvent == null) {
continue;
}
eventFilter.getEventHandleThread(event).submit(() -> obj.onReceiveEvent(stickyEvent));
}
}
}
public void unregister(EventSubscriber obj) {
assert obj != null;
EventFilter eventFilter = mFilterMap.remove(obj);
if (eventFilter != null) {
List<Class<?>> eventList = eventFilter.getEventList();
for (Class<?> event : eventList) {
List<EventSubscriber> list = mEventMap.get(event);
if (list != null) {
list.remove(obj);
}
}
}
}
public void post(Object event) {
post(event, false);
}
public void removeSticky(Class<?> event) {
mStickyMap.remove(event);
}
public void post(Object event, boolean sticky) {
assert event != null;
Class<?> eventClass = event.getClass();
if (sticky) {
mStickyMap.put(eventClass, wrapEvent(event));
}
List<EventSubscriber> objectList = mEventMap.get(eventClass);
if (objectList == null) {
//dead event
//post(NoSubScribeEvent(event));
return;
}
try {
readWriteLock.writeLock().lock();
mPostingQueue.offer(event);
} finally {
readWriteLock.writeLock().unlock();
}
if (mPosting) {
return;
}
mPosting = true;
boolean empty = mPostingQueue.isEmpty();
while (!empty) {
Object object;
try {
readWriteLock.readLock().lock();
object = mPostingQueue.poll();
empty = mPostingQueue.isEmpty();
} finally {
readWriteLock.readLock().unlock();
}
if (object == null) {
break;
}
//publish
Event wrapEvent = wrapEvent(object);
for (EventSubscriber subscriber : objectList) {
EventFilter eventFilter = mFilterMap.get(subscriber);
assert eventFilter != null;
eventFilter.getEventHandleThread(eventClass).submit(() -> subscriber.onReceiveEvent(wrapEvent));
}
}
mPosting = false;
}
private Event wrapEvent(Object object) {
Event event = new Event();
event.payload = object;
event.type = object.getClass();
return event;
}
}
Test
package eventbus;
public class Test implements EventSubscriber {
public static void main(String[] args) throws InterruptedException {
EventFilter eventFilter = new EventFilter();
eventFilter.addEvent(String.class, EventHandleThread.BACKGROUND, true, 0);
EventBus.getInstance().register(new Test(), eventFilter);
for (int i = 0; i < 100; i++) {
EventBus.getInstance().post("this is event" + i);
}
Thread.sleep(2000);
}
@Override
public void onReceiveEvent(Event event) {
System.out.println(event.type + " " + event.payload + " Thread=" + Thread.currentThread());
}
}