使用RxBus有一段時間了,記得剛使用的使用我在ActivityB中去注冊RxBus,這個時候ActivityB還沒有啟動,然后我在ActivityA中post事件后啟動ActivityB,然而ActivityB并沒有接收到任何事件,當時還郁悶了很久。
ActivityB接收不到事件的主要原因在于我們的ActivityB create之前發送了事件這個時候由于我們ActivityB并沒有創建,所以也沒有進行事件注冊,當然收不到任何事件了,我當時的做法是調用handler.postDelay(500)進行延遲一下發送事件,這個時間是我測試ActivityB完全創建后的時間,這個時候由于ActivityB已經創建并且注冊了事件,ActivityA發送的事件也可以被接收了,但是這種做法并不優雅靠譜,因為Activity創建時間多少完全不可預料,導致我們沒辦法去估計一個準確的延遲事件,并且這種做法實在不優雅。。。。
所以我一直在想難道事件只能在Activity創建后才能發送嗎?可以不可在它創建前就發送,創建成功后接受到呢?
我們看下面實現代碼
/**
* Created by wubo on 2017/6/2.
*/
public class RxBus<T> {
private static RxBus mRxBus;
private PublishProcessor<T> mPublishProcessor;
private ConcurrentHashMap<Class, T> mConcurrentHashMap;
private RxBus() {
mPublishProcessor = PublishProcessor.create();
mConcurrentHashMap = new ConcurrentHashMap();
}
public static RxBus getInstance() {
if (mRxBus == null) {
synchronized (RxBus.class) {
if (mRxBus == null) {
mRxBus = new RxBus();
}
}
}
return mRxBus;
}
//register
public Flowable<T> registerEvent(Class<T> clazz) {
Flowable <T>tFlowable = mPublishProcessor.ofType(clazz);
return tFlowable;
}
public void post(T event) {
mPublishProcessor.onNext(event);
}
public Flowable registerStickEvent(final Class<T> clazz) {
final T t = mConcurrentHashMap.get(clazz);
if (t != null) {
Flowable<T> tFlowable = mPublishProcessor.ofType(clazz);
return tFlowable.mergeWith(Flowable.create(new FlowableOnSubscribe<T>() {
@Override
public void subscribe(FlowableEmitter<T> e) throws Exception {
e.onNext(t);
}
}, BackpressureStrategy.BUFFER));
}
return mPublishProcessor;
}
public void postStick(T event) {
T t = mConcurrentHashMap.get(event.getClass());
if (t == null) {
mConcurrentHashMap.put(event.getClass(), event);
}
post(event);
}
public void removeAllStickEvent(){
mConcurrentHashMap.clear();
}
}
我們可以這么寫,為什么Stick事件注冊的時候需要合并一個流呢,因為如果我們這里首先需要返回一個Flowable提供訂閱的,那么如果我們直接返回mPublishProcessor的話我們怎么接收到Stick事件呢? 所以我們merge一個流當我們注冊時就發送我們map中存入的stick事件。
這里還有個小問題,當我們訂閱一個被觀察者我們銷毀的時候需要解除訂閱,否則會繼續持有引用對象,導致內存泄露.
Flowable<String> flowable = RxBus.getInstance().registerStickEvent(String.class);
flowable.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Log.e("wwwSecondActivity",s);
}
});
比如這段代碼,我們在SecondActivity中使用RxBus注冊一個Stick事件,然后在MainActivity中開開心心的調用
RxBus.getInstance().postStick("MainActivity");
發送一個Stick事件
好我們看看Log打印日志
點擊按鈕跳到第二個界面
06-03 16:15:06.051 22060-22060/com.example.boboyuwu.rxbusstick E/wwwSecondActivity: MainActivity
吆西貌似效果不錯哦~~等等我們按返回鍵退出這個界面然后再進來試一試
06-03 16:16:46.714 22060-22060/com.example.boboyuwu.rxbusstick E/wwwSecondActivity: MainActivity
06-03 16:16:46.749 22060-22060/com.example.boboyuwu.rxbusstick E/wwwSecondActivity: MainActivity
這。。這怎么回事,我只發送一次,怎么收到二個事件抱著大大的?我返回這個界面再進來一次看看,
06-03 16:18:00.767 22060-22060/com.example.boboyuwu.rxbusstick E/wwwSecondActivity: MainActivity
06-03 16:18:00.767 22060-22060/com.example.boboyuwu.rxbusstick E/wwwSecondActivity: MainActivity
06-03 16:18:00.814 22060-22060/com.example.boboyuwu.rxbusstick E/wwwSecondActivity: MainActivity
可以看到情況很不妙每次我們退出界面再進來的時候這個事件就會多接收一次一直累加,我們分析一下問題可能出現在哪。
在上一篇關于RxBus實現思考我們分析到當我們使用subject onNext()去發送事件時候我們會發送到我們所有用subject 訂閱
的里面,而我們第一次接收一次說明我們訂閱了一次,而退出來再進來接收到二次說明我們訂閱了二次,可你會疑惑我界面都退出了啊,不錯,Rx是不會主動幫你去解除你的訂閱關系的,所以當你第一次訂閱的時候雖然你退出了但是被訂閱者引用還在RxBus的Subject里,每訂閱并退出一次,這個引用會增加一個,所以當你發送事件的時候滿足類型會接收之前所有訂閱過的事件。
哪這多坑啊怎么辦呢?沒事rx可能也想到一點幫我提供一個CompositeDisposable類,它可以組裝我們所有的訂閱關系,然后統一進行解綁,我們可以創建一個方法
public void addDispose(Disposable disposable){
mCompositeDisposable.add(disposable);
}
然后在onDesdory()中進行統一解綁
@Override
protected void onDestroy() {
super.onDestroy();
if(mCompositeDisposable!=null&&!mCompositeDisposable.isDisposed()){
mCompositeDisposable.dispose();
}
RxBus.getInstance().removeAllStickEvent();
}
這樣不管我們怎么退出界面都沒有關系,我們當前注冊事件的界面只會一對一的接收發送過來的事件。
isDisposed()是2.0新方法判斷CompositeDisposable有沒有解綁
如果已經解綁就會返回true如果沒有的話就返回false
我們看看源碼是不是這樣
@Override
public void dispose() {
if (disposed) {
return;
}
OpenHashSet<Disposable> set;
synchronized (this) {
if (disposed) {
return;
}
disposed = true;
set = resources;
resources = null;
}
dispose(set);
}
@Override
public boolean isDisposed() {
return disposed;
}
isDisposed()返回disposed變量而當我們調用dispose()丟棄我們綁定關系后 disposed = true;會被賦值為true
@Override
public boolean add(Disposable d) {
ObjectHelper.requireNonNull(d, "d is null");
if (!disposed) {
synchronized (this) {
if (!disposed) {
OpenHashSet<Disposable> set = resources;
if (set == null) {
set = new OpenHashSet<Disposable>();
resources = set;
}
set.add(d);
return true;
}
}
}
d.dispose();
return false;
}
add方法也是如此如果disposed是true的話說明已經解綁就不添加到集合里,直接把參數解綁然后返回false說明添加失敗.