打造屬于自己的RxBus

RxBus

通過RxJava實現Rxbus。

相信大家已經非常熟悉EventBus了。最近正在學習Rxjava,如果在項目中已經使用了Rxjava,使用RxBus來代替EventBus應該是不錯的選擇。

RxJava最核心的兩個東西是Observables(被觀察者,事件源)和Subscribers(觀察者)。Observables發出一系列事件,Subscribers處理這些事件。

RxBus工作原理

直接看代碼

Note that it is important to subscribe to the exact same rxBus instance that was used to post the events

采用單例模式來保證rxBus對象一致

public class RxBus {

    private static RxBus rxBus;
    private final Subject<Object, Object> _bus = new SerializedSubject<>(PublishSubject.create());

    private RxBus() {
    }

    public static RxBus getInstance() {
        if (rxBus == null) {
            synchronized (RxBus.class) {
                if (rxBus == null) {
                    rxBus = new RxBus();
                }
            }
        }
        return rxBus;
    }


    public void send(Object o) {
        _bus.onNext(o);
    }

    public Observable<Object> toObserverable() {
        return _bus;
    }
}

Activity中發送事件

public void sendTap(View view){   
         RxBus.getInstance().send(new TapEvent());
}

public void sendOther(View view){
        RxBus.getInstance().send(new OtherEvent());
}

Fragment中接收事件

 RxBus.getInstance().toObserverable()
                .subscribe(new Action1<Object>() {
                    @Override
                    public void call(Object o) {
                        if (o instanceof TapEvent) {
                            textView.setText("tap");
                        } else if (o instanceof OtherEvent) {
                            textView.setText("other");
                        }
                    }
                });

效果


效果圖

以上就是使用Rxjava簡單實現RxBus的功能,當然這還遠遠不夠

RxBus升級

在具體使用過程中總會碰到各種各樣的問題

場景1
我在上一個項目中實現了無限輪播的功能,并且希望輪播圖在用戶滑動、不可見、以及程序在后臺休眠時都停止滾動,這時候就希望EventBus及時的傳遞這3種狀態,為此我需要寫slideEvent、visibleEvent、aliveEvent3個類,雖然他們都需要傳遞一個簡單的Boolen值。

解決方案
創建一個Event“管家”
類似key-value的方式,每個事件都有自己的唯一的Code,接收事件時根據Code返回對應的content

public class Events<T> {

    //所有事件的CODE
    public static final int TAP = 1; //點擊事件
    public static final int OTHER = 21; //其它事件
    
    //枚舉
    @IntDef({TAP, OTHER})
    @Retention(RetentionPolicy.SOURCE)
    public @interface EventCode {}


    public @Events.EventCode int code;
    public T content;

    public static <O> Events<O> setContent(O t) {
        Events<O> events = new Events<>();
        events.content = t;
        return events;
    }

    public <T> T getContent() {
        return (T) content;
    }

}

場景2
怎么又內存泄漏了?

每個人在開發過程中,或多或少都會碰到內存泄漏的的問題,我一直有一個天真的想法,RxJava那么牛逼,是不是能無聲無息地就能解決內存泄漏的問題了,答案是否定的。

我看了不少有關RxJava的文章,都會提到
一定要記得在生命周期結束的地方取消訂閱事件,防止RxJava可能會引起的內存泄漏問題。

你可以

@Overrideprotected void onDestroy() { 
        super.onDestroy(); 
        if(!rxSubscription.isUnsubscribed()) {     
            rxSubscription.unsubscribe();
         }
}

又或者

使用CompositeSubscription把 Subscription 收集到一起,方便 Activity(基類) 銷毀時取消訂閱,防止內存泄漏。

前者可以在任一生命周期階段取消訂閱,缺點是每個acivity/fragment都要重寫方法。

后者可以寫在BaseActivity(大家都不會陌生),每個activity都能用,缺點是不夠靈活。

以上兩種方法似乎都欠缺點意思,所幸Rx家族“人丁興旺“”,早已想好了解決方案
RxLifecycle

一、bindToLifecycle()方法
在子類使用Observable中的compose操作符,調用,完成Observable發布的事件和當前的組件綁定,實現生命周期同步。從而實現當前組件生命周期結束時,自動取消對Observable訂閱。

 Observable.interval(1, TimeUnit.SECONDS)
        .compose(this.bindToLifecycle())
            .subscribe(new Action1<Long>() { 
                @Override
                public void call(Long num) {
                    Log.i(TAG, "  " +num);
                }
            });

二、bindUntilEvent() 方法
使用ActivityEvent類,其中的CREATE、START、 RESUME、PAUSE、STOP、 DESTROY分別對應生命周期內的方法。使用bindUntilEvent指定在哪個生命周期方法調用時取消訂閱。

public enum ActivityEvent {

    CREATE,
    START,
    RESUME,
    PAUSE,
    STOP,
    DESTROY

}


public enum FragmentEvent {

    ATTACH,
    CREATE,
    CREATE_VIEW,
    START,
    RESUME,
    PAUSE,
    STOP,
    DESTROY_VIEW,
    DESTROY,
    DETACH

}

組裝零件

public class RxBus {

    private static RxBus rxBus;
    private final Subject<Events<?>, Events<?>> _bus = new SerializedSubject<>(PublishSubject.<Events<?>>create());

    private RxBus(){}

    public static RxBus getInstance(){
        if (rxBus == null){
            synchronized (RxBus.class){
                if (rxBus == null){
                    rxBus = new RxBus();
                }
            }
        }
        return rxBus;
    }

    public void send(Events<?> o) {
        _bus.onNext(o);
    }

    public void send(@Events.EventCode int code, Object content){
        Events<Object> event = new Events<>();
        event.code = code;
        event.content = content;
        send(event);
    }

    public Observable<Events<?>> toObservable() {
        return _bus;
    }

    public static SubscriberBuilder with(FragmentLifecycleProvider provider){
        return new SubscriberBuilder(provider);
    }

    public static SubscriberBuilder with(ActivityLifecycleProvider provider){
        return new SubscriberBuilder(provider);
    }


    public static class SubscriberBuilder{

        private FragmentLifecycleProvider mFragLifecycleProvider;
        private ActivityLifecycleProvider mActLifecycleProvider;
        private FragmentEvent mFragmentEndEvent;
        private ActivityEvent mActivityEndEvent;
        private int event;
        private Action1<? super Events<?>> onNext;
        private Action1<Throwable> onError;

        public SubscriberBuilder(FragmentLifecycleProvider provider) {
            this.mFragLifecycleProvider = provider;
        }

        public SubscriberBuilder(ActivityLifecycleProvider provider){
            this.mActLifecycleProvider = provider;
        }

        public SubscriberBuilder setEvent(@Events.EventCode int event){
            this.event = event;
            return this;
        }

        public SubscriberBuilder setEndEvent(FragmentEvent event){
            this.mFragmentEndEvent = event;
            return this;
        }

        public SubscriberBuilder setEndEvent(ActivityEvent event){
            this.mActivityEndEvent = event;
            return this;
        }

        public SubscriberBuilder onNext(Action1<? super Events<?>> action){
            this.onNext = action;
            return this;
        }

        public SubscriberBuilder onError(Action1<Throwable> action){
            this.onError = action;
            return this;
        }


        public void create(){
            _create();
        }

        public Subscription _create(){
            if (mFragLifecycleProvider!=null){
                return RxBus.getInstance().toObservable()
                        .compose(mFragmentEndEvent == null ? mFragLifecycleProvider.bindToLifecycle() :mFragLifecycleProvider.<Events<?>>bindUntilEvent(mFragmentEndEvent)) // 綁定生命周期
                        .filter(new Func1<Events<?>, Boolean>() {
                            @Override
                            public Boolean call(Events<?> events) {
                                return events.code == event;
                            }
                        })   //過濾 根據code判斷返回事件
                        .subscribe(onNext, onError == null ? new Action1<Throwable>() {
                            @Override
                            public void call(Throwable throwable) {
                                throwable.printStackTrace();
                            }
                        } : onError);
            }
            if (mActLifecycleProvider!=null){
                return RxBus.getInstance().toObservable()
                        .compose(mActivityEndEvent == null ? mActLifecycleProvider.bindToLifecycle() :mActLifecycleProvider.<Events<?>>bindUntilEvent(mActivityEndEvent))
                        .filter(new Func1<Events<?>, Boolean>() {
                            @Override
                            public Boolean call(Events<?> events) {
                                return events.code == event;
                            }
                        })
                        .subscribe(onNext, onError == null ? (Action1<Throwable>) new Action1<Throwable>() {
                            @Override
                            public void call(Throwable throwable) {
                                throwable.printStackTrace();
                            }
                        } : onError);
            }
            return null;
        }
    }
}

新BUS上路

依然使用前面的例子

Activity中發送事件

 public void sendTap(View view){
        RxBus.getInstance().send(Events.TAP, "Tap傳了一個String");
    }

    public void sendOther(View view){
        RxBus.getInstance().send(Events.OTHER, null);
//        RxBus.getInstance().send(Events.OTHER, new OtherEvent("Cloud", 25));
    }

Fragment中接收事件

fragment需要繼承RxLifecycle對應組件

public class BlankFragment extends RxFragment {}
 
  RxBus.with(this)
                .setEvent(Events.TAP)
//                .setEndEvent(FragmentEvent.DESTROY_VIEW) //不設置默認與fragment生命周期同步
                .onNext(new Action1<Events<?>>() {
                    @Override
                    public void call(Events<?> events) {
                        String content = events.getContent();
                        textView.setText(content);
                    }
                })
                .create();

        RxBus.with(this)
                .setEvent(Events.OTHER)
                .setEndEvent(FragmentEvent.DESTROY_VIEW) //不設置默認與fragment生命周期同步
                .onNext(new Action1<Events<?>>() {
                    @Override
                    public void call(Events<?> events) {
                        OtherEvent event = events.getContent();
                        textView.setText("Name: "  + event.getName() + ",Age: "+ event.getAge());
                    }
                })
                .onError(new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        textView.setText(throwable.toString());
                    }
                }) // 異常處理,默認捕獲異常,不做處理,程序不會crash。
                .create();

效果

![normal.gif](http://upload-images.jianshu.io/upload_images/1896628-f4eb87351f734b10.gif?imageMogr2/auto-orient/strip)

完整代碼,請移步

參考資料

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,554評論 2 379

推薦閱讀更多精彩內容