RxJava的設(shè)計原理以及在項目中的實際應(yīng)用

目的

??相信做過Android開發(fā)的同學(xué)都在項目中使用過Rxjava這個庫,它可以化繁為簡,讓代碼更簡潔、更清晰。這篇文章我將簡要介紹RxJava的基本原理以及在項目中使用到的一些常用功能。當(dāng)然,對于RxJava如何使用并不是本文討論的重點,我主要想由此及彼,探討我們在碰到一些優(yōu)秀的三方庫的時候,應(yīng)該怎樣去學(xué)習(xí),去使用,能夠讓我們個人的專業(yè)技能有所提高。

基本原理

定義

RxJava官網(wǎng)是這樣介紹的:

RxJava:a library for composing asynchronous and event-based programs using observable sequences for the Java VM
翻譯:RxJava 是一個在 Java VM 上使用可觀測的序列來組成異步的、基于事件的程序的庫

原理
  • RxJava: 基于 一種擴(kuò)展的觀察者模式
  • RxJava的擴(kuò)展觀察者模式中有4個角色:
角色 作用
被觀察者(Observable) 產(chǎn)生事件
觀察者(Observer) 接收事件,并給出響應(yīng)動作
訂閱(Subscribe) 連接 被觀察者 & 觀察者, 相當(dāng)于注冊監(jiān)聽
事件(Event) 被觀察者 & 觀察者 溝通的載體
  • 使用
        Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                emitter.onNext(1);
                emitter.onComplete();
            }
        })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer integer) throws Exception {
                        Log.d(TAG, "accept integer = " + integer);
                    }
                });
  • 說明:上面的使用很簡單,鏈?zhǔn)浇Y(jié)構(gòu),有種一氣呵成的感覺,看起來很舒服,其實做了很多事情
  1. 創(chuàng)建被觀察者,發(fā)送事件,即emitter.onNext(1),發(fā)送了數(shù)字1;
  2. 指定被觀察者的執(zhí)行線程,Schedulers.io();
  3. 觀察者處理接收到的數(shù)據(jù);
  4. 切換線程到主線程,觀察者將在主線程處理這些數(shù)據(jù)。
  • 總結(jié)
  1. RxJava一個核心功能就是線程切換,可以看到,使用簡單,代碼看起來很簡潔、明了;
  2. 鏈?zhǔn)浇Y(jié)構(gòu),很熟悉吧,平時我們創(chuàng)建Dialog的時候,就是鏈接結(jié)構(gòu);

在項目中的實際應(yīng)用

??所有的庫都是以項目的實際需求為導(dǎo)向,如果在項目中沒法使用,那這個庫就沒任何意義了。RxJava也是如此,之所以有這么多人使用,就是因為項目里面很多操作,不管是簡單的還是復(fù)雜的操作,都可以使用RxJava很簡潔的實現(xiàn),當(dāng)然,前提是你要熟悉RxJava。
??可能大家會有疑問,前面介紹的使用很簡單,沒什么精彩的地方,不管怎么看,也不像能在項目中大量的使用。是的,如果只是這樣,確實沒有什么值得推崇的。RxJava強大的地方,不僅在于線程切換的方便,更重要的是,它有非常多的操作符,可以實現(xiàn)項目中比較復(fù)雜的操作。關(guān)于這些操作符的詳細(xì)解讀,可以閱讀我專門寫的一篇文章:操作符全解

下面列舉一些在項目中使用很多的用例:

1. 功能防抖
  • 使用Rxjava很簡單,這里可以防止用戶點擊登錄按鈕過快,打開多個登錄頁面的問題
        RxView.clicks(layoutLogin)
                .throttleFirst(1, TimeUnit.SECONDS)
                .subscribe(new Consumer<Unit>() {
                    @Override
                    public void accept(Unit unit) throws Exception {
                        
                    }
                });
2. 定時器
  • 藍(lán)牙項目使用,延時500ms處理一些事務(wù)
        Observable.timer(500, TimeUnit.MILLISECONDS)
             .subscribeOn(Schedulers.io())
             .subscribe(new Consumer<ArrayList<XmBluetoothDeviceInfo>>() {
            @Override
            public void accept(ArrayList<XmBluetoothDeviceInfo> xmBluetoothDeviceInfos) throws Exception {
                DialogManager.getInstance().askForDismiss(dialogController, true);
                if (ObjectUtils.isNotEmpty(xmBluetoothDeviceInfos)) {
                    startDeviceDetails(edrDevice, xmBluetoothDeviceInfos);
                } else {
                    ToastUtils.showShort(StringUtils.getString(R.string.xm_data_error));
                    finish();
                }
            }
        })
3.監(jiān)聽器
  • 在項目中使用最多,替代了以前我們通過Listener的方式處理監(jiān)聽,這里的好處很多:調(diào)用方便;線程切換簡單;不用處理添加和刪除時的同步問題等等
    private final PublishSubject mPublishSubject = PublishSubject.create();
    public Disposable registerSessionEvent(Consumer<SessionEvent> onNext) {
        Disposable disposable = mPublishSubject.observeOn(AndroidSchedulers.mainThread()).subscribe(onNext);
        mCompositeDisposable.add(disposable);
        return disposable;
    }
4.連續(xù)點擊N次,每次間隔M毫秒。兩個操作符搞定,如果自己寫的話就比較復(fù)雜了。
        mCompositeDisposable.add(observable.buffer(observable.debounce(300, TimeUnit.MILLISECONDS))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(units -> {
                    if (units != null && units.size() >= 5) {
                        mTestConfigBtn.setVisibility(View.VISIBLE);
                        DebugConfig.enableDebug();
                    }
                }));
5. RxBus
  • 事件總線,可直接代替EventBus、廣播的使用,在項目中也是使用的非常多
        val d = RxBus.getInstance().register(ChangeBgEvent::class.java)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe { changeBgEvent -> activity.setTitleColor(changeBgEvent.color) }

??以上只是項目中使用到的列舉的很少一部分,實際用到了很多??梢钥吹剑_實用簡潔的代碼實現(xiàn)了比較復(fù)雜的功能,提升了開發(fā)效率。

我們應(yīng)該如何學(xué)習(xí)

??既然三方庫能夠給我們的開發(fā)帶來很大的效率提升,我們當(dāng)然推薦使用。但是如何使用卻是值得探討,是不是只要會用就行了呢?個人覺得絕對不能只是簡單的知道它的用法就行,我們不僅要知其然,還要知其所以然,這樣才能理解它的原理,使用起來更加得心應(yīng)手,不至于使用出錯,在碰到問題的時候解決起來也更加順利。
下面是我對于學(xué)習(xí)使用三方庫的一些看法:

熟練基本用法
  • 這是最基本的要求。一般三方庫都會有使用文檔,我們可以通讀一遍,也可以在網(wǎng)上看別人寫的一些文章,學(xué)習(xí)別人對于一些用法的理解。
學(xué)習(xí)源碼

??這個比較關(guān)鍵,也是個人比較推薦的做法,我們只有看懂了源碼,才能理解它的原理,并學(xué)習(xí)它的一些優(yōu)秀的框架和設(shè)計

  1. 注釋
  • 源碼里面有對代碼的詳細(xì)解釋,還有使用用例,看這些注釋加深對接口的理解,有些注釋,看一遍就知道使用使用了
 * Example usage:
 * <pre> {@code

  PublishSubject<Object> subject = PublishSubject.create();
  // observer1 will receive all onNext and onComplete events
  subject.subscribe(observer1);
  subject.onNext("one");
  subject.onNext("two");
  // observer2 will only receive "three" and onComplete
  subject.subscribe(observer2);
  subject.onNext("three");
  subject.onComplete();

  // late Observers only receive the terminal event
  subject.test().assertEmpty();
  } </pre>
  1. 裝飾者模式
  • 庫里面大量用到了裝飾者模式,這是RxJava的核心,也是我們能夠很簡潔使用的關(guān)鍵。
abstract class AbstractObservableWithUpstream<T, U> extends Observable<U> implements HasUpstreamObservableSource<T> {

    /** The source consumable Observable. */
    protected final ObservableSource<T> source;

    /**
     * Constructs the ObservableSource with the given consumable.
     * @param source the consumable Observable
     */
    AbstractObservableWithUpstream(ObservableSource<T> source) {
        this.source = source;
    }

    @Override
    public final ObservableSource<T> source() {
        return source;
    }

}
  1. 鏈?zhǔn)浇Y(jié)構(gòu)
  • Rxjava的使用通過鏈?zhǔn)浇Y(jié)構(gòu)直接完成,簡潔明了,對我們平時的代碼設(shè)計有很好的借鑒意義,下面是藍(lán)牙的同事使用的案例,實現(xiàn)了比較復(fù)雜的邏輯(省略了具體實現(xiàn))
        Disposable subscribe = getMaybe(bluetoothDeviceExt)
                .map(new Function<TargetResponseWrap, XmHistoryDeviceInfo>() {
                    @Override
                    public XmHistoryDeviceInfo apply(TargetResponseWrap targetResponseWrap) throws Exception {
                        ......
                    }
                })
                .map(new Function<XmHistoryDeviceInfo, XmBluetoothDeviceInfo>() {
                    @Override
                    public XmBluetoothDeviceInfo apply(XmHistoryDeviceInfo xmHistoryDeviceInfo) throws Exception {
                        ......
                    }
                })
                .flatMap(new Function<XmBluetoothDeviceInfo, MaybeSource<XmBluetoothDeviceInfo>>() {
                    @Override
                    public MaybeSource<XmBluetoothDeviceInfo> apply(final XmBluetoothDeviceInfo deviceInfo) throws Exception {
                        ......
                    }
                })
                .doOnSuccess(new Consumer<XmBluetoothDeviceInfo>() {
                    @Override
                    public void accept(XmBluetoothDeviceInfo xmBluetoothDeviceInfo) throws Exception {
                        ......
                    }
                })
                .doOnSuccess(new Consumer<XmBluetoothDeviceInfo>() {
                    @Override
                    public void accept(XmBluetoothDeviceInfo xmBluetoothDeviceInfo) throws Exception {
                        ......
                    }
                })
                .flatMap(new Function<XmBluetoothDeviceInfo, MaybeSource<XmBluetoothDeviceInfo>>() {
                    @Override
                    public MaybeSource<XmBluetoothDeviceInfo> apply(final XmBluetoothDeviceInfo deviceInfo) throws Exception {
                        ......
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<XmBluetoothDeviceInfo>() {
                    @Override
                    public void accept(XmBluetoothDeviceInfo xmBluetoothDeviceInfo) throws Exception {
                        ......
                    }
                });

??當(dāng)然,RxJava里面還有很多值得開發(fā)者學(xué)習(xí)的地方。如果不能理解充分,很可能會誤用,在引入項目初期,也有一些使用不當(dāng)?shù)牡胤?,包括我自己,后面熟悉之后就好多了?/p>

分享總結(jié)
  • 我們經(jīng)常說,完成一件事情或任務(wù),要學(xué)會總結(jié),這樣才能更加深刻。對于我們軟件開發(fā)者而言也是一樣的,學(xué)習(xí)之后,能夠分享,能夠總結(jié),其好處不言而喻。

??以上是我在項目中使用Rxjava的一些介紹和使用心得。以及由此引出的引入三方庫之后,我們應(yīng)該如何使用和學(xué)習(xí)的一些粗淺看法。一句話,就是在引入一個庫后,能夠?qū)W習(xí)它背后的原理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容