guava EventBus 觀察者模式
主要有主題和觀察者,主題發生變化時,通知觀察者(用戶只要提供觀察者,並且向主題中註冊即可),其中在操作subscribersByType都開啓鎖了。所以這個EventBus 主要提供線程安全和註解的方式簡化代碼。
1. 向EventBus 註冊觀察者時,找出這個觀察者的帶有@Subscribe 的方法,並且這個方法只有一個參數。
可以@AllowConcurrentEvents來不同步的,沒有的就同步來執行方法
向subscribersByType 註冊這個參數類型和方法 ,即下面的listen的參數類型Long和listen方法
2. 發佈消息
根據發佈的對象類型,從subscribersByType 找出方法,
把這些方法先放到隊列中,然後再一個個執行。
- 建觀察者,這裏的訂閱方法只要加上@Subscribe即可
public class LongListener {
@Subscribe
public void listen(Long integer) { …}
}
- 註冊觀察者併發布消息
EventBus eventBus = new EventBus("test");
LongListener n = new LongListener();
eventBus.register(n);
eventBus.post(new Long(1));
//根據註解 來找到一些方法,再根據參數類型來確定哪些觀察者
- 具體看下EventBus
class EventBus {
private final ReadWriteLock subscribersByTypeLock = new ReentrantReadWriteLock();//這個是同步
private final SetMultimap<Class<?>, EventSubscriber> subscribersByType = HashMultimap.create();//value是個集合,存放觀察者
private final SubscriberFindingStrategy finder = new AnnotatedSubscriberFinder();// 尋找觀察者的幫助類
private final **ThreadLocal**<Queue<EventWithSubscriber>> eventsToDispatch =
new ThreadLocal<Queue<EventWithSubscriber>>();//存放要執行的觀察者,這個是線程安全
Public EventBus(String str) {
this.exceptionHandler = new Longging..Exception(str);//報錯信息
}
**//向這個主題中註冊觀察者**
public void register(Object object) {
Multimap<Class<?>, EventSubscriber> methodsInListener = finder.findAllSubscribers(object);
subscribersByTypeLock.writeLock().lock();//開啓寫鎖,其他線程不讀不寫
try {
subscribersByType.putAll(methodsInListener);
} finally {
subscribersByTypeLock.writeLock().unlock();//釋放寫鎖
}
}
**//註銷觀察者**
public void unregister(Object object) {
Multimap<Class<?>, EventSubscriber> methodsInListener = finder.findAllSubscribers(object);
//下面代碼就省略了,主要就是開啓寫鎖,從methodsInListener 去掉object,然後關閉寫鎖
....
}
**//這個就相當於通知觀察者 event 是個類型,根據這個類型找到觀察者**
public void post(Object event) {
Set<Class<?>> dispatchTypes = TypeToken.of(event.getClass()).getTypes().rawTypes();//這個類的父類和父接口
//遍歷dispatchTypes,並且開啓讀鎖,其他線程只讀不寫
Set<EventSubscriber> wrappers = subscribersByType.get(eventType);//取出這個類的觀察者
//遍歷wrappers 將wrapper加到執行觀察者隊列中
eventsToDispatch.get().offer(new EventWithSubscriber(event, wrapper));
//關閉讀鎖
if (!dispatched && !(event instanceof DeadEvent))
post(new DeadEvent(this, event));
//執行隊列中的觀察者(遍歷隊列中的,執行觀察者並從隊列中移除)
try {
Queue<EventWithSubscriber> events = eventsToDispatch.get();
eventWithSubscriber.subscriber.handleEvent(eventWithSubscriber.event);
//這個就是利用反射調用方法
} finally {
eventsToDispatch.remove();//刪除隊列
}
}
}
- AnnotatedSubscriberFinder 主要根據類型來找出符合條件的方法(觀察者中有@Subscribe的方法的第一個參數類型 – 和這個方法)
class AnnotatedSubscriberFinder implements SubscriberFindingStrategy {
//獲取 觀察者的參數類型和對應的方法(@Subscribe)
public Multimap<Class<?>, EventSubscriber> findAllSubscribers(Object listener) {
Multimap<Class<?>, EventSubscriber> methodsInListener = HashMultimap.create();
//從緩存中獲取符合條件的方法
for (Method method : subscriberMethodsCache.getUnchecked(listener.getClass())) {
EventSubscriber subscriber= null;
//這裏就是同步
if(method.getAnnotation(AllowConcurrentEvents.class) != null)
subscriber = new EventSubscriber(listener, method);
else
subscriber = new SynchronizedEventSubscriber(listener, method);
methodsInListener.put(**method.getParameterTypes()[0]**, subscriber);//是這個方法的第一個參數類型
}
}
return methodsInListener;
}
//就是獲取這個類(包括父類,父接口)的帶有@Subscribe並且只有一個參數類型的
private static ImmutableList<Method> getAnnotatedMethodsInternal(Class<?> clazz) {
Map<MethodIdentifier, Method> identifiers = Maps.newHashMap();
for (Class<?> superClazz : TypeToken.of(clazz).getTypes().rawTypes()) {
for (Method superClazzMethod : superClazz.getMethods()) {
if (superClazzMethod.isAnnotationPresent(**Subscribe.class**)) {
Class<?>[] parameterTypes = superClazzMethod.getParameterTypes();
if (parameterTypes.length != 1) throw new Ex....(...);
...//存放到identifiers
}
}
}
return ImmutableList.copyOf(identifiers.values());
}
//採用沒有引用了就刪除的緩存,主要是存放這個類和方法
private static final LoadingCache<Class<?>, ImmutableList<Method>> subscriberMethodsCache = CacheBuilder.newBuilder().weakKeys().build(new CacheLoader<Class<?>, ImmutableList<Method>>() {
public ImmutableList<Method> load(Class<?> concreteClass) throws Exception {
return getAnnotatedMethodsInternal(concreteClass);
}
});
}