目的
??相信做過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),有種一氣呵成的感覺,看起來很舒服,其實做了很多事情
- 創(chuàng)建被觀察者,發(fā)送事件,即emitter.onNext(1),發(fā)送了數(shù)字1;
- 指定被觀察者的執(zhí)行線程,Schedulers.io();
- 觀察者處理接收到的數(shù)據(jù);
- 切換線程到主線程,觀察者將在主線程處理這些數(shù)據(jù)。
- 總結(jié)
- RxJava一個核心功能就是線程切換,可以看到,使用簡單,代碼看起來很簡潔、明了;
- 鏈?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è)計
- 注釋
- 源碼里面有對代碼的詳細(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>
- 裝飾者模式
- 庫里面大量用到了裝飾者模式,這是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;
}
}
- 鏈?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í)它背后的原理。