打造屬于自己的RxBus

RxBus

通過(guò)RxJava實(shí)現(xiàn)Rxbus峡扩。

相信大家已經(jīng)非常熟悉EventBus了踱蠢。最近正在學(xué)習(xí)Rxjava汁汗,如果在項(xiàng)目中已經(jīng)使用了Rxjava撮抓,使用RxBus來(lái)代替EventBus應(yīng)該是不錯(cuò)的選擇妇斤。

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

RxBus工作原理

直接看代碼

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

采用單例模式來(lái)保證rxBus對(duì)象一致

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中發(fā)送事件

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簡(jiǎn)單實(shí)現(xiàn)RxBus的功能乖酬,當(dāng)然這還遠(yuǎn)遠(yuǎn)不夠

RxBus升級(jí)

在具體使用過(guò)程中總會(huì)碰到各種各樣的問(wèn)題

場(chǎng)景1
我在上一個(gè)項(xiàng)目中實(shí)現(xiàn)了無(wú)限輪播的功能死相,并且希望輪播圖在用戶滑動(dòng)、不可見(jiàn)咬像、以及程序在后臺(tái)休眠時(shí)都停止?jié)L動(dòng)算撮,這時(shí)候就希望EventBus及時(shí)的傳遞這3種狀態(tài),為此我需要寫(xiě)slideEvent县昂、visibleEvent肮柜、aliveEvent3個(gè)類,雖然他們都需要傳遞一個(gè)簡(jiǎn)單的Boolen值倒彰。

解決方案
創(chuàng)建一個(gè)Event“管家”
類似key-value的方式审洞,每個(gè)事件都有自己的唯一的Code,接收事件時(shí)根據(jù)Code返回對(duì)應(yīng)的content

public class Events<T> {

    //所有事件的CODE
    public static final int TAP = 1; //點(diǎn)擊事件
    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;
    }

}

場(chǎng)景2
怎么又內(nèi)存泄漏了待讳?

每個(gè)人在開(kāi)發(fā)過(guò)程中芒澜,或多或少都會(huì)碰到內(nèi)存泄漏的的問(wèn)題,我一直有一個(gè)天真的想法创淡,RxJava那么牛逼痴晦,是不是能無(wú)聲無(wú)息地就能解決內(nèi)存泄漏的問(wèn)題了,答案是否定的琳彩。

我看了不少有關(guān)RxJava的文章誊酌,都會(huì)提到
一定要記得在生命周期結(jié)束的地方取消訂閱事件,防止RxJava可能會(huì)引起的內(nèi)存泄漏問(wèn)題汁针。

你可以

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

又或者

使用CompositeSubscription把 Subscription 收集到一起术辐,方便 Activity(基類) 銷毀時(shí)取消訂閱砚尽,防止內(nèi)存泄漏施无。

前者可以在任一生命周期階段取消訂閱,缺點(diǎn)是每個(gè)acivity/fragment都要重寫(xiě)方法必孤。

后者可以寫(xiě)在BaseActivity(大家都不會(huì)陌生)猾骡,每個(gè)activity都能用瑞躺,缺點(diǎn)是不夠靈活。

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

一、bindToLifecycle()方法
在子類使用Observable中的compose操作符嫂便,調(diào)用捞镰,完成Observable發(fā)布的事件和當(dāng)前的組件綁定,實(shí)現(xiàn)生命周期同步毙替。從而實(shí)現(xiàn)當(dāng)前組件生命周期結(jié)束時(shí)岸售,自動(dòng)取消對(duì)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分別對(duì)應(yīng)生命周期內(nèi)的方法腻惠。使用bindUntilEvent指定在哪個(gè)生命周期方法調(diào)用時(shí)取消訂閱。

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;
                            }
                        })   //過(guò)濾 根據(jù)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中發(fā)送事件

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

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

Fragment中接收事件

fragment需要繼承RxLifecycle對(duì)應(yīng)組件

public class BlankFragment extends RxFragment {}
 
  RxBus.with(this)
                .setEvent(Events.TAP)
//                .setEndEvent(FragmentEvent.DESTROY_VIEW) //不設(shè)置默認(rèn)與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) //不設(shè)置默認(rèn)與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());
                    }
                }) // 異常處理欲虚,默認(rèn)捕獲異常妖枚,不做處理,程序不會(huì)crash苍在。
                .create();

效果

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

完整代碼绝页,請(qǐng)移步

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市寂恬,隨后出現(xiàn)的幾起案子续誉,更是在濱河造成了極大的恐慌,老刑警劉巖初肉,帶你破解...
    沈念sama閱讀 221,430評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酷鸦,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡牙咏,警方通過(guò)查閱死者的電腦和手機(jī)臼隔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)妄壶,“玉大人摔握,你說(shuō)我怎么就攤上這事《〖模” “怎么了氨淌?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,834評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵泊愧,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我盛正,道長(zhǎng)删咱,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,543評(píng)論 1 296
  • 正文 為了忘掉前任豪筝,我火速辦了婚禮痰滋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘续崖。我一直安慰自己即寡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布袜刷。 她就那樣靜靜地躺著聪富,像睡著了一般。 火紅的嫁衣襯著肌膚如雪著蟹。 梳的紋絲不亂的頭發(fā)上墩蔓,一...
    開(kāi)封第一講書(shū)人閱讀 52,196評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音萧豆,去河邊找鬼奸披。 笑死,一個(gè)胖子當(dāng)著我的面吹牛涮雷,可吹牛的內(nèi)容都是我干的阵面。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼洪鸭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼样刷!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起览爵,我...
    開(kāi)封第一講書(shū)人閱讀 39,671評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤置鼻,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后蜓竹,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體箕母,經(jīng)...
    沈念sama閱讀 46,221評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評(píng)論 3 340
  • 正文 我和宋清朗相戀三年俱济,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嘶是。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,444評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蛛碌,死狀恐怖聂喇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情左医,我是刑警寧澤授帕,帶...
    沈念sama閱讀 36,134評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站浮梢,受9級(jí)特大地震影響跛十,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜秕硝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評(píng)論 3 333
  • 文/蒙蒙 一芥映、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧远豺,春花似錦奈偏、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,285評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至棺滞,卻和暖如春裁蚁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背继准。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,399評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工枉证, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人移必。 一個(gè)月前我還...
    沈念sama閱讀 48,837評(píng)論 3 376
  • 正文 我出身青樓室谚,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親崔泵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子秒赤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容