这篇文章,打算 换个编写的方式去编写。旨在改变写法,提升质量。
目录结构如下
- 源码简介(大致讲述环境)
- 这些问题怎么实现?(带着问题去看源码)
- 流程图级目录结构(讲函数的用途)
- 函数解析(讲如何实现)
- 小结(讲最简洁的 核心思想&个人理解)
- 带着问题继续前行
源码简介
RxBus版本: v2.0.1
源码地址: github链接
简介:
- RxBus与EventBus功能上差不多,RxBus主要没有发送粘性事件的功能。
- 这是一个和EventBus类似的项目,但是依赖RxJava RxAndroid
- 这是基于RxJava,成功的二次开发。
这些问题怎么实现?
- 如何实现线程间切换?
- 如何实现线程调度?
- 能支持哪些功能?
- 事件是怎么进行派发的?
- 事件又是怎么被接收到的?
流程图级目录结构
这个章节,我们大致的了解下 整个源码的脉络。
对于大型开源项目,就更加需要整理出其中的脉络。当然难度就更大一些。
有个大致的了解后,就能带着问题进一步思考。
RxBus:提供get()方法获取单例Bus的实例
public static synchronized Bus get() {
if (sBus == null) {
sBus = new Bus(ThreadEnforcer.ANY);
}
return sBus;
}
Bus就是注册、注销、发送消息 三个函数
RxBus 并不是一个框架,而是一种使用 RxJava 实现事件总线的一种解决思路,实质是交由 RxJava 去实现。
RxBus最明显的优势或许:是它非常好的展示了利用RxJava做二次开发的优势。
函数解析
先看注册register部分代码。为了避免篇幅过大,我把这个函数
拆开一段一段的分析。
以下代码皆来自 register 函数。
register函数
检查所在线程
if (object == null) {
throw new NullPointerException("Object to register must not be null.");
}
enforcer.enforce(this);
enforcer是检查所在线程 。默认是ANY。这里并不是主干逻辑。
看代码要适可而止,该停下就停下;否则,容易抓不住 主干。
初始化Producers
看到Producer,不忙看具体的逻辑。
看一下段,有Subscriber的字样。说明这可能是生产者消费者,那你就有数了。
finder.findAllProducers 是什么呢?
通过反射机制,获取所有的Produce。
Map<EventType, ProducerEvent> foundProducers = finder.findAllProducers(object);
for (EventType type : foundProducers.keySet()) {
final ProducerEvent producer = foundProducers.get(type);
ProducerEvent previousProducer = producersByType.putIfAbsent(type, producer);
//checking if the previous producer existed
if (previousProducer != null) {
throw new IllegalArgumentException("Producer method for type " + type
+ " found on type " + producer.getTarget().getClass()
+ ", but already registered by type " + previousProducer.getTarget().getClass() + ".");
}
Set<SubscriberEvent> subscribers = subscribersByType.get(type);
if (subscribers != null && !subscribers.isEmpty()) {
for (SubscriberEvent subscriber : subscribers) {
dispatchProducerResult(subscriber, producer);
}
}
}
初始化Subscribers
结合上面部分,就简单了。同理这是找到所有的Subscribers。
Map<EventType, Set<SubscriberEvent>> foundSubscribersMap = finder.findAllSubscribers(object);
for (EventType type : foundSubscribersMap.keySet()) {
Set<SubscriberEvent> subscribers = subscribersByType.get(type);
if (subscribers == null) {
//concurrent put if absent
Set<SubscriberEvent> SubscribersCreation = new CopyOnWriteArraySet<>();
subscribers = subscribersByType.putIfAbsent(type, SubscribersCreation);
if (subscribers == null) {
subscribers = SubscribersCreation;
}
}
final Set<SubscriberEvent> foundSubscribers = foundSubscribersMap.get(type);
if (!subscribers.addAll(foundSubscribers)) {
throw new IllegalArgumentException("Object already registered.");
}
}
设置订阅事件监听
for (Map.Entry<EventType, Set<SubscriberEvent>> entry : foundSubscribersMap.entrySet()) {
EventType type = entry.getKey();
ProducerEvent producer = producersByType.get(type);
if (producer != null && producer.isValid()) {
Set<SubscriberEvent> subscriberEvents = entry.getValue();
for (SubscriberEvent subscriberEvent : subscriberEvents) {
if (!producer.isValid()) {
break;
}
if (subscriberEvent.isValid()) {
dispatchProducerResult(subscriberEvent, producer);
}
}
}
}
看源码,除了了解实现逻辑,register,有很明显的 分块。
foundSubscribers、foundSubscribers、设置event事件监听。
每一块的意图非常清晰
。这是值得借鉴的
所谓顺眼的代码,有一个特点:逻辑清晰,井然有序
看源码,不能单纯找答案
,站在一个更高的角度去评审
代码,收获自然更大。
unregister函数
检查所在线程
if (object == null) {
throw new NullPointerException("Object to unregister must not be null.");
}
enforcer.enforce(this);
enforcer是检查所在线程 。默认是ANY。这里并不是主干逻辑。
看着代码,我们很多时候,都是用return就直接处理掉了。
这是我们对Exception的运用太少导致。
必要的地方需要抛出异常来解决,这是做SDK必要的检测加强
注销Producers
找到所有的Producers,从producersByType表中移除。
Map<EventType, ProducerEvent> producersInListener = finder.findAllProducers(object);
for (Map.Entry<EventType, ProducerEvent> entry : producersInListener.entrySet()) {
final EventType key = entry.getKey();
ProducerEvent producer = getProducerForEventType(key);
ProducerEvent value = entry.getValue();
if (value == null || !value.equals(producer)) {
throw new IllegalArgumentException(
"Missing event producer for an annotated method. Is " + object.getClass()
+ " registered?");
}
producersByType.remove(key).invalidate();
}
producersByType是ConcurrentMap,存储了所有注册的Producers,可以通过EventType来找到。
增加一个点,ConcurrentMap的实现原理是什么? 为什么是16个分段锁,更多行不行?是否是线程安全的?如果用他,什么情况下就不是线程安全的了?
注销Subscribers
这段逻辑也很清晰,找到所有的subscriber,调用invalidate()废弃它;remove掉,等于是干掉强引用。
Map<EventType, Set<SubscriberEvent>> subscribersInListener = finder.findAllSubscribers(object);
for (Map.Entry<EventType, Set<SubscriberEvent>> entry : subscribersInListener.entrySet()) {
Set<SubscriberEvent> currentSubscribers = getSubscribersForEventType(entry.getKey());
Collection<SubscriberEvent> eventMethodsInListener = entry.getValue();
if (currentSubscribers == null || !currentSubscribers.containsAll(eventMethodsInListener)) {
throw new IllegalArgumentException(
"Missing event subscriber for an annotated method. Is " + object.getClass()
+ " registered?");
}
for (SubscriberEvent subscriber : currentSubscribers) {
if (eventMethodsInListener.contains(subscriber)) {
subscriber.invalidate();
}
}
currentSubscribers.removeAll(eventMethodsInListener);
}
post函数
检查所在线程
if (event == null) {
throw new NullPointerException("Event to post must not be null.");
}
enforcer.enforce(this);
enforcer是检查所在线程 。默认是ANY。这里并不是主干逻辑。
调用dispatch进行event派发
Set<Class<?>> dispatchClasses = flattenHierarchy(event.getClass());
boolean dispatched = false;
for (Class<?> clazz : dispatchClasses) {
Set<SubscriberEvent> wrappers = getSubscribersForEventType(new EventType(tag, clazz));
if (wrappers != null && !wrappers.isEmpty()) {
dispatched = true;
for (SubscriberEvent wrapper : wrappers) {
dispatch(event, wrapper);
}
}
}
处理没有派发成功的event
将没有派发成功的Event,按照DeadEvent进行处理
if (!dispatched && !(event instanceof DeadEvent)) {
post(new DeadEvent(this, event));
}
那这里就需要看下DeadEvent的描述
Wraps an event that was posted, but which had no subscribers and thus could not be delivered.
发出了一个没有订阅者的event时间。永远都收不到。
小结
分析完毕。
看了几个类,搞了半天,啥都没说啊。
这也是看源码,最容易犯的错误。看了个大概,却不知道到底是什么原理?
很多人看源码,就止步在这里。
了解一样东西,你需要做到
(一)
了解背景,立项之本
(二)提出问题,明确你想要了解
(三)避免一头扎进去,先看目录,了解框架
(四)步步为营,梳理脉络,找到主干线
(五)从阅读找答案,别懒,记下来
以本文为例,我们对比来说
(一)还能做的更好,比如更新周期啥的,看以后还会不会更新。
(二)这里还可以➕跟同类型项目的差别。最大的劣势是什么?
(三)看代码,要适可而止,有些分支脉络,可以很深。合理安排时间
(四)逐步细化脉络
(五)瞎了,这是完全没做。
带着问题继续前行
之前的都归属于 理清 主干 脉络。但距离理解RxBus还是有距离的。
对细节需要有一定的把握。
再看一遍register函数
初始化Producers
Map<EventType, ProducerEvent> foundProducers = finder.findAllProducers(object);
for (EventType type : foundProducers.keySet()) {
final ProducerEvent producer = foundProducers.get(type);
ProducerEvent previousProducer = producersByType.putIfAbsent(type, producer);
//checking if the previous producer existed
if (previousProducer != null) {
throw new IllegalArgumentException("Producer method for type " + type
+ " found on type " + producer.getTarget().getClass()
+ ", but already registered by type " + previousProducer.getTarget().getClass() + ".");
}
Set<SubscriberEvent> subscribers = subscribersByType.get(type);
if (subscribers != null && !subscribers.isEmpty()) {
for (SubscriberEvent subscriber : subscribers) {
dispatchProducerResult(subscriber, producer);
}
}
}
我们需要注意几个
finder.findAllProducers实现了什么?
(一)finder是初始化时传入的
(二)finder没有指明,默认值是Finder.ANNOTATED
(三)findAllProducers 调用AnnotatedFinder.findAllProducers(listener)来实现
(四)findAllProducers部分
final Class<?> listenerClass = listener.getClass();
Map<EventType, ProducerEvent> producersInMethod = new HashMap<>();
Map<EventType, SourceMethod> methods = PRODUCERS_CACHE.get(listenerClass);
if (null == methods) {
methods = new HashMap<>();
loadAnnotatedProducerMethods(listenerClass, methods);
}
if (!methods.isEmpty()) {
for (Map.Entry<EventType, SourceMethod> e : methods.entrySet()) {
ProducerEvent producer = new ProducerEvent(listener, e.getValue().method, e.getValue().thread);
producersInMethod.put(e.getKey(), producer);
}
}
return producersInMethod;
- PRODUCERS进行了缓存
- loadAnnotatedProducerMethods(不放开讲了)是利用注解,拿到了处理函数、Tag、操作所在的线程
- 最终将listener(可以预见很多时候是Activity、Fragment)、produce的函数、操作所在线程thread这三样,封装进ProductEvent,保存到了producersInMethod
producersByType.putIfAbsent(type, producer)代表了什么?
putIfAbsent保存在previousProducer变量中,如果返回 null,表示 map 里面没有要查找的 key-value mapping。
这判断是否已经有现成的key。就是说这个类型,不能重复设置。
dispatchProducerResult做了什么?
看看这个函数做了什么
private void dispatchProducerResult(final SubscriberEvent subscriberEvent, ProducerEvent producer) {
producer.produce().subscribe(new Action1<Object>() {
@Override
public void call(Object event) {
if (event != null) {
dispatch(event, subscriberEvent);
}
}
});
}
从背景调查中,得到RxBus是依赖RxJava的。看到subscribe就得有点数了。
还需要看producer.produce() 和 dispatch(event, subscriberEvent);
看producer.produce()做了什么?
public Observable produce() {
return Observable.create(new Observable.OnSubscribe<Object>() {
@Override
public void call(Subscriber<? super Object> subscriber) {
try {
subscriber.onNext(produceEvent());
subscriber.onCompleted();
} catch (InvocationTargetException e) {
throwRuntimeException("Producer " + ProducerEvent.this + " threw an exception.", e);
}
}
}).subscribeOn(EventThread.getScheduler(thread));
}
Observable 链式调用,处理produceEvent。同时指定了thread。
dispatch(event, subscriberEvent);
/**
* Dispatches {@code event} to the subscriber in {@code wrapper}. This method is an appropriate override point for
* subclasses that wish to make event delivery asynchronous.
*
* @param event event to dispatch.
* @param wrapper wrapper that will call the handle.
*/
protected void dispatch(Object event, SubscriberEvent wrapper) {
if (wrapper.isValid()) {
wrapper.handle(event);
}
}
看描述,就有逼数了。在wrapper中将event发送给订阅者。这个method是否异步由子类来定义。
public void handle(Object event) {
subject.onNext(event);
}
subject是什么鬼?搜一下就知道了
private void initObservable() {
subject = PublishSubject.create();
subject.onBackpressureBuffer().observeOn(EventThread.getScheduler(thread))
.subscribe(new Action1<Object>() {
@Override
public void call(Object event) {
try {
if (valid) {
handleEvent(event);
}
} catch (InvocationTargetException e) {
throwRuntimeException("Could not dispatch event: " + event.getClass() + " to subscriber " + SubscriberEvent.this, e);
}
}
});
}
是SubscriberEvent 在结构函数中调用的,初始化Observable的。
之后就是handleEvent。
...不重要的code
}
try {
method.invoke(target, event);
} catch (IllegalAccessException e) {
throw new AssertionError(e);
} catch (InvocationTargetException e) {
if (e.getCause() instanceof Error) {
throw (Error) e.getCause();
}
throw e;
}
又是反射调用method。
停一停,好像有点数了
现在,我们可以回答一部分问题了。 请切换到 《真的小结了》 这部分继续阅读《从register函数中获取到的》这部分标签下面的部分
看完了《真的小结了》
套路就清楚了。
初始化Subscribers这部分代码。就是缓存Subscribe供unregister和post时使用
发现了个有趣的地方
for (Map.Entry<EventType, Set<SubscriberEvent>> entry : foundSubscribersMap.entrySet()) {
EventType type = entry.getKey();
ProducerEvent producer = producersByType.get(type);
if (producer != null && producer.isValid()) {
Set<SubscriberEvent> subscriberEvents = entry.getValue();
for (SubscriberEvent subscriberEvent : subscriberEvents) {
if (!producer.isValid()) {
break;
}
if (subscriberEvent.isValid()) {
dispatchProducerResult(subscriberEvent, producer);#有趣的地方
}
}
}
}
- register执行的是初始化,注册。
- dispatchProducerResult(subscriberEvent, producer);#有趣的地方
- 上面这行代码的两个参数都是从producersByType来的。
- foundSubscribersMap,即subscribersByType,且只提供了EventType。
- register函数,先初始化producersByType,并通过subscribersByType 也获取了一次Set
- 然后,再初始化subscribersByType。
- 换言之,第一次初始化producersByType,subscribersByType应该为空。dispatchProducerResult也就是不会被调用的。
- 所以,这个register函数,只有第二个dispatchProducerResult,才会真正生效的地方。
缓冲一下,讲unregister
这个函数,好像没什么好说的。
就是,把 producersByType和subscribersByType都释放掉。释放资源,去除强引用。
有兴趣的读者,可以看下。
真的小结了
如何实现线程间切换?
从register函数中获取到的
如果从RxBus这个框架的角度来看,这个RxBus的线程切换,是利用RxJava实现的。
thread会通过注解来定义的。
在RxBus进行register时,被注解定义的thread会被获取封装成ProducerEvent,在设置event事件监听时,通过RxJava的subscribeOn函数生效。
可能你要说这个线程切换还是没说啊。Liar
这个道理跟 开发边界 是一样的。总是有些界限的
你必须要知道 何时停止调研
否则,你容易调入自己挖的逻辑深渊
如何实现线程调度?
从register函数中获取到的
这个问题,也就同上了,RxJava的范畴
能支持哪些功能?
从register函数中获取到的
- 支持注解方式
- 不支持Sticky,因为event都是immediate处理的
事件是怎么进行派发的?
从post函数中获取到的
event是通过post来发送的。这里也是利用了register中描述的rxjava的onBackpressureBuffer缔造的“队列”,准确的说是背压策略。
背压,具体措施是下游观察者通知上游的被观察者发送事件
背压策略很好的解决了异步环境下被观察者和观察者速度不一致的问题
RxJava 2.0内部提供 封装了背压策略模式的方法
onBackpressureBuffer()
onBackpressureDrop()
onBackpressureLatest()
事件又是怎么被接收到的?
从register函数中获取到的
- 系统维护了EventType和ProducerEvent的关系Map
- 在dispatch中,调用了RxJava的subscribe函数,设置了回调。
- subscribe函数,又调用了onNext方法进行触发。
- 为了
实现消息列队
,在SubscriberEvent初始化时,使用了RxJava的onBackpressureBuffer - onBackpressureBuffer把observable发送出来的事件做缓存,当request方法被调用的时候,给下层流发送一个item
- 最终method又通过反射调用的方式,被触发。
给大家
看完了全部,还知道了什么呢。
RxBus代码量很少。很小。如果有RxJava引入的话,应该是个还不错的选择。
如果觉得不满意,完全也可以自己实现一个。
这篇文章,讲了
- 如何进行消息缓存?
- 如何进行事件监听回调?
- 如何调用method?
- 编码过程中,要注意的知识点。
- 个人发现了一个小问题。
- 以及如何看一份第三方工具的源码。还是要有套路的。
希望大家能多给点建议。基本上RxBus我觉得差不多就是这样。
关于注解部分就不详细讲解了。