我们都知道当有了RxBus这种通信方式后,我们Activity A跳转到Activity B,然后B带值传到A中就很方便了,再也不需要之前的startActivityForResualt.
可是假如我们A带值到B去怎么做呢,RxBus是做不到的,传统的方式我们还在使用Intent 传过去,虽然这样做没有什么毛病,但是无疑当业务复杂后,key会特别的多.
那我就想能不能改进RxBus方式可以达到这种效果呢?
首先我们简单看一下网上一个RxBus实现源码,并不复杂
public class RxBus {
// 主题
private final FlowableProcessor<Object> bus;
// PublishSubject只会把在订阅发生的时间点之后来自原始Flowable的数据发射给观察者
private RxBus() {
bus = PublishProcessor.create().toSerialized();
}
public static RxBus getDefault() {
return RxBusHolder.sInstance;
}
private static class RxBusHolder {
private static final RxBus sInstance = new RxBus();
}
// 提供了一个新的事件
public void post(Object o) {
bus.onNext(o);
}
// 根据传递的 eventType 类型返回特定类型(eventType)的 被观察者
public <T> Flowable<T> toFlowable(Class<T> eventType) {
return bus.ofType(eventType);
}
// 封装默认订阅
public <T> Disposable toDefaultFlowable(Class<T> eventType, Consumer<T> act) {
return bus.ofType(eventType).compose(RxUtil.<T>rxSchedulerHelper()).subscribe(act);
}
}
我之前用的RxBus实现方式是这样的当然还是1.x+版本
public class RxBus {
private static final String TAG = RxBus.class.getSimpleName();
private static Object async = new Object();
private static RxBus instance;
public static boolean DEBUG = false;
private ConcurrentHashMap<Object, List<Subject>> subjectMapper = new ConcurrentHashMap<>();
private RxBus() {}
public static RxBus get() {
synchronized (async){
if (null == instance)
instance = new RxBus();
return instance;
}
}
/**
* 注册事件
* @param tag
* @param clazz
* @param <T>
* @return
*/
@SuppressWarnings("unchecked")
public <T> Observable<T> register(@NonNull Object tag, @NonNull Class<T> clazz) {
List<Subject> subjectList = subjectMapper.get(tag);
if (null == subjectList) {
subjectList = new ArrayList<>();
subjectMapper.put(tag, subjectList);
}
Subject<T> subject;
subjectList.add(subject = PublishSubject.create());
if (DEBUG) Log.d(TAG, "[register]subjectMapper: " + subjectMapper);
return subject;
}
/**
* 反注册事件
* @param tag
* @param observable
*/
public void unregister(@NonNull Object tag, @NonNull Observable observable) {
List<Subject> subjects = subjectMapper.get(tag);
if (null != subjects) {
subjects.remove(observable);
if (isEmpty(subjects)) {
subjectMapper.remove(tag);
}
}
if (DEBUG) Log.d(TAG, "[unregister]subjectMapper: " + subjectMapper);
}
/**
* 清空所有
*/
public void clear(){
if (subjectMapper != null)
subjectMapper.clear();
}
/**
* 发送事件
* @param content
*/
public void post(@NonNull Object content) {
post(content.getClass().getName(), content);
}
/**
* 发送事件
* @param tag
* @param content
*/
@SuppressWarnings("unchecked")
public void post(@NonNull Object tag, @NonNull Object content) {
List<Subject> subjectList = subjectMapper.get(tag);
if (!isEmpty(subjectList)) {
for (Subject subject : subjectList) {
subject.onNext(content);
}
}
if (DEBUG) Log.d(TAG, "[send]subjectMapper: " + subjectMapper);
}
private boolean isEmpty(List<Subject> subjectList) {
return (subjectList == null || subjectList.size() == 0);
}
}
对比一下可以看出来,之前的方式是创建一个map,map的key会存我们RxBus注册的事件也就是我们传进去的Key,而value却是一个List集合,
这种实现方式是这样的: 我们创建一个Map,然后在register中我们为map中的每一个value都创建一个List,当注册的时候根据Key拿到里面List,List如果不为空的话我们就create一个PublishSubject然后把它存进List中,这样每个Key对应的value(List)可能会有 0~N个PublishSubject,在post发送事件的时候根据key拿到map中的List然后遍历List调用每一个PublishSubject的
onNext()把事件发送出去. 这样实现是没什么问题,但是不仅有点麻烦,而且我们每次注册都需要create去创建一个PublishSubject,并且由于我们的RxBus是static静态的导致我们List中强引用每个对象所以我们调用结束后还需要unregister去反注册一下,感觉有点麻烦啊。
这样相比之下第一种方式比较简洁一些,第一种方式我们只创建一个PublishProcessor,所有注册RxBus的地方公用这一个PublishProcessor,
而我们post发送事件的时候也会把事件发送给每一个使用这个PublishProcessor注册的地方
我们看看PublishProcessor onNext()源码
@Override
public void onNext(T t) {
if (subscribers.get() == TERMINATED) {
return;
}
if (t == null) {
onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
return;
}
for (PublishSubscription<T> s : subscribers.get()) {
s.onNext(t);
}
}
我大胆猜测,我们在调用PublishProcessor 的subscribe()方法的时候就会把里面的参数传到类似一个集合中,然后在调用onNext()时候会统一调用集合中所有值的onNext(),我们看看subscribers.get()中subscribers是啥???
/** The array of currently subscribed subscribers. */
final AtomicReference<PublishSubscription<T>[]> subscribers;
关于AtomicReference我们看一个解释这里暂时不做深究,只需要知道它只是一个原子性对象引用就好
AtomicReference
AtomicReference类提供了一种读和写都是原子性的对象引用变量。原子意味着多个线程试图改变同一个AtomicReference(例如比较和交换操作)将不会使得AtomicReference处于不一致的状态。AtomicReferenc的compareAndSet()方法可以使得它与期望的一个值进行比较,如果他们是相等的,AtomicReference里的对象会被设置成一个新的引用。
可以看到这里引用到的是一个PublishSubscription数组,现在来搜一下什么地方赋值这个数组了看看能不能搜到,
/**
* Tries to add the given subscriber to the subscribers array atomically
* or returns false if the subject has terminated.
* @param ps the subscriber to add
* @return true if successful, false if the subject has terminated
*/
boolean add(PublishSubscription<T> ps) {
for (;;) {
PublishSubscription<T>[] a = subscribers.get();
if (a == TERMINATED) {
return false;
}
int n = a.length;
@SuppressWarnings("unchecked")
PublishSubscription<T>[] b = new PublishSubscription[n + 1];
System.arraycopy(a, 0, b, 0, n);
b[n] = ps;
if (subscribers.compareAndSet(a, b)) {
return true;
}
}
}
咦貌似在这个地方传递PublishSubscription进去然后正好赋值给我们上面的PublishSubscription数组
看看add()方法在哪被添加
@Override
public void subscribeActual(Subscriber<? super T> t) {
PublishSubscription<T> ps = new PublishSubscription<T>(t, this);
t.onSubscribe(ps);
if (add(ps)) {
// if cancellation happened while a successful add, the remove() didn't work
// so we need to do it again
if (ps.isCancelled()) {
remove(ps);
}
} else {
Throwable ex = error;
if (ex != null) {
t.onError(ex);
} else {
t.onComplete();
}
}
}
在这个方法中Subscriber参数和this被组装成一个PublishSubscription对象然后调用上面的add()方法传递进去
ok我们接下来回头看一看PublishProcessor 的subscribe()方法里做了些什么
@BackpressureSupport(BackpressureKind.UNBOUNDED_IN)
@SchedulerSupport(SchedulerSupport.NONE)
public final Disposable subscribe() {
return subscribe(Functions.emptyConsumer(), Functions.ON_ERROR_MISSING,
Functions.EMPTY_ACTION, FlowableInternalHelper.RequestMax.INSTANCE);
}
点进去看看
@CheckReturnValue
@BackpressureSupport(BackpressureKind.SPECIAL)
@SchedulerSupport(SchedulerSupport.NONE)
public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError,
Action onComplete, Consumer<? super Subscription> onSubscribe) {
ObjectHelper.requireNonNull(onNext, "onNext is null");
ObjectHelper.requireNonNull(onError, "onError is null");
ObjectHelper.requireNonNull(onComplete, "onComplete is null");
ObjectHelper.requireNonNull(onSubscribe, "onSubscribe is null");
LambdaSubscriber<T> ls = new LambdaSubscriber<T>(onNext, onError, onComplete, onSubscribe);
subscribe(ls);
return ls;
}
LambdaSubscriber<T> ls = new LambdaSubscriber<T>(onNext, onError, onComplete, onSubscribe);
这里面是把onNext, onError, onComplete, onSubscribe上面的参数封装进LambdaSubscriber对象里
public LambdaSubscriber(Consumer<? super T> onNext, Consumer<? super Throwable> onError,
Action onComplete,
Consumer<? super Subscription> onSubscribe) {
super();
this.onNext = onNext;
this.onError = onError;
this.onComplete = onComplete;
this.onSubscribe = onSubscribe;
}
好上面貌似就一个subscribe(ls)方法,不是很复杂点进去看一看
@BackpressureSupport(BackpressureKind.SPECIAL)
@SchedulerSupport(SchedulerSupport.NONE)
@Experimental
public final void subscribe(FlowableSubscriber<? super T> s) {
ObjectHelper.requireNonNull(s, "s is null");
try {
Subscriber<? super T> z = RxJavaPlugins.onSubscribe(this, s);
ObjectHelper.requireNonNull(z, "Plugin returned null Subscriber");
subscribeActual(z);
} catch (NullPointerException e) { // NOPMD
throw e;
} catch (Throwable e) {
Exceptions.throwIfFatal(e);
// can't call onError because no way to know if a Subscription has been set or not
// can't call onSubscribe because the call might have set a Subscription already
RxJavaPlugins.onError(e);
NullPointerException npe = new NullPointerException("Actually not, but can't throw other exceptions due to RS");
npe.initCause(e);
throw npe;
}
}
擦这里正好调用了上面分析的subscribeActual(z)方法,然后调用add()方法把参数组装成PublishSubscription然后加入到数组引用中,根据源码分析结果跟我们之前的猜测完全一致!
分析完毕,总结一下,第一种写法弊端在于create出大量的Subject对象比较耗用内存。
优点是我们使用hashmap存我们每个事件的key,我们可以制定只发送给注册某一个Key的List中的每一个事件。
第二种写法有点在于公用同一个Subject,占用内存小.
缺点是我们是根据事件的类型来存储发送事件的,如果几个界面注册了相同类型事件,那么发送这个事件所有的界面都会收到。
现在写法在于我们知道了Rx源码中就已经帮我们把注册的对象添加到数组中并且在我们调用onNext()的时候会帮我们遍历进行发送所以我们是不需要自己再创建数组去引用的,这样就不需要手动去移除了.