當 Kotlin 遇見 RxJava 多資料來源
溫馨提醒
閱讀本文最好有Kotlin基礎,若沒有基礎,可參考之前文章Kotlin初探,使用Kotlin優雅的開發Android應用,以及RxJava基礎(本文基於RxJava2),當然我也會盡可能詳細解釋讓你順利閱讀本文。
寫在前面
最近幾天回過頭,看了之前的總結RxJava操作符系列,感覺對Rxjava多資料來源的處理不是很理解,所以在總結學習一波。大家都知道,最近Kotlin語言一直佔據熱搜榜,褒貶不一,但我想說,不管有什麼想法都要拋在腦後,畢竟Google爸爸出手,你不情願也要跟隨它的步伐。鑑於此,本篇對RxJava多資料來源的總結是基於Kotlin語言,也讓大家明白,使用Kotlin開發應用並不是不能使用Java庫,現在有一部分人擔心,Kotlin第三方庫那麼少,如果使用Kotlin開發那不是給自己找罪受,其實你完全錯了,當你說這話的時候,我敢斷定你都還沒有接觸Kotlin,因為Koltin有一個最重要的優勢就是和Java絕對相容。
多資料來源處理操作符
在RxJava中多資料來源處理的操作符很多,但是最經典的就要數merge,contact,zip了。如果對這三個操作符不是很熟悉的話,可以去檢視它的使用,當然如果你懶得去看,我也會簡單提一下。merge操作符可以處理多個Observable傳送的資料,它是一個非同步操作,不保證資料傳送的順序,即有可能出現資料交叉,當一個Observable傳送了onError後,未執行的Observable不在繼續執行,直接執行merge的onError方法。
contact操作符執行時一個同步操作,嚴格按照contact中傳入Observable先後執行,即前面的先執行後面的後執行,並且最終傳送的資料也是有序的,即第一個Observable的資料傳送完畢再傳送第二個,依次類推。
zip操作符和contact和merge有了本質的區別,它會將每個Observable個資料項分佈對應返回一個Observable再傳送,最終傳送的資料量與最小資料長度相同。
使用場景分析
假如現在我們有三種商品,有一個查詢商品資訊的介面,根據介面可以查詢該商品的價格以及出售地點。商品實體類
data class Goods(var id:Int,var price: Int, var address: String)
在Kotlin語言中,實體類建立用data class 關鍵詞,我們不需要和Java一樣建立get/set方法,只需一行程式碼搞定。
建立模擬網路請求
object NetRequest { //模擬網路請求 fun getGoodsObservable(id: Int): Observable<Goods> { fun getGoodsObservable(id: Int): Observable<Goods> { return Observable.create { source -> Thread.sleep(Random().nextInt(1000).toLong()) var data = Goods(id, Random().nextInt(20), "地址${id}") source.onNext(data) source.onComplete() Log.e("getGoodsObservable:", "${id}") } } }
在上面我們建立了一個單例類,在Kotlin中使用object修飾類時即給我們自動建立了一個單例物件。在每一句程式碼結尾我們不需要再和Java一樣寫一個分號“;”來結束,什麼也不用寫。
Observable.create使用的是lambda表示式,在Kotlin語言中是支援lambda表示式的。source 就是ObservableEmitter<Goods>,所以我們可以呼叫onNext傳送資料。為了更準確的模擬網路請求,使用Thread.sleep隨機的延遲,模擬網路請求的時間。
fun getGoodsObservable(id: Int): Observable<Goods> { return Observable.create { source -> Thread.sleep(Random().nextInt(1000).toLong()) var data = Goods(id, Random().nextInt(20), "地址${id}") source.onNext(data) source.onComplete() Log.e("getGoodsObservable:", "${id}") }
當然由於subscribe只有一個引數,所以我們也可以這樣寫。也就是省略了source ->,此時it就表示該引數資料。
return Observable.create { Thread.sleep(Random().nextInt(1000).toLong()) var data = Goods(id, Random().nextInt(20), "地址${id}") it.onNext(data) it.onComplete() Log.e("getGoodsObservable:", "${id}") }
在java中實現如下
return Observable.create(new ObservableOnSubscribe<Goods>() { @Override public void subscribe(@NonNull ObservableEmitter<Goods> e) throws Exception { //處理邏輯 } });
merge
準備好了請求操作,開始使用merge看看執行的效果。
fun executeMerge() { Observable.merge(getGoodsObservable(1).subscribeOn(Schedulers.newThread()), getGoodsObservable(2).subscribeOn(Schedulers.newThread()), getGoodsObservable(3).subscribeOn(Schedulers.newThread())) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .toList() .subscribe({ Log.e(TAG, it.toString()) }, { Log.e(TAG, it.toString()) }) }
merge中有三個網路請求操作,並通過subscribeOn(Schedulers.newThread())將網路請求切換到執行緒中執行,資料都請求成功後,再通過observeOn(AndroidSchedulers.mainThread())切換到主執行緒請求資料。為了三請求都成功後,我們在更新UI,所以通過toList()將請求的資料轉換成List一塊傳送。在上面的subscribe依然使用的lambda表示式,subscribe({},{})中第一個括號是onSuccess回撥,裡面的it是接收到的List< Goods >資料,第二個括號是onError回撥,it表示異常Throwable物件。
subscribe部分Java程式碼
.subscribe(new Consumer<List<Goods>>() { @Override public void accept(@NonNull List<Goods> goodses) throws Exception { } }, new Consumer<Throwable>() { @Override public void accept(@NonNull Throwable throwable) throws Exception { } });
當然如果你想使用RxJava2中onSubscribe(@NonNull Disposable d) ,你可以這樣使用subscribe
.subscribe(object : SingleObserver<List<Goods>> { override fun onSubscribe(d: Disposable?) { } override fun onError(e: Throwable?) { } override fun onSuccess(t: List<Goods>?) { } })
為了觀察,我們將請求成功的資料顯示在介面上,我們建立一個Button,TextView。
class MainActivity : AppCompatActivity(), View.OnClickListener { val TAG = "MainActivity" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) //加入這句import kotlinx.android.synthetic.main.activity_main.* //不用再findViewById,可直接使用 merge.setOnClickListener(this) } override fun onClick(v: View) { when (v.id) { R.id.merge -> { executeMerge() } } //when 關鍵字和Java中的Switch關鍵詞是類似的, //只不過它比Java中的Switch強大的多,可以接收任何引數, //然後判斷使用,也可以如下使用 when (v) { merge -> { } } } }
contact
我們點選執行幾次發現,返回的List的資料並不是按照merge引數的先後順序執行的,它是併發的,最終的順序,是由網路請求的快慢決定的,請求返回資料越快也就表示該資料最早傳送,即在List中最靠前。那麼此時出現一個問題,如果我想返回資料的List順序嚴格按照位置的先後順序呢?那此時使用merge的話,是不太現實了。當然前面我們提到contact可以使用。那麼直接將merge更改為contact執行以下試試,
fun executeContact() { Observable.concat(getGoodsObservable(1).subscribeOn(Schedulers.newThread()), getGoodsObservable(2).subscribeOn(Schedulers.newThread()), getGoodsObservable(3).subscribeOn(Schedulers.newThread())) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .toList() .subscribe({ Log.e(TAG, it.toString()) }, { Log.e(TAG, it.toString()) }) }
的確,發現無論執行多少次List的資料都能按照contact中Observable順序傳送,我們想要的效果可以實現了,不過你會發現,效率太差了,這是同步執行啊,只有第一個請求成功,才會去請求第二個,然後第三個,假如一次請求需要一秒,那三次請求至少三秒啊,不能忍。
zip
鑑於上面兩種方式的利弊,如果我們既想如merge一樣併發執行,又想和contact一樣保證順序,是不是有點強迫症的意思,當然強大的zip就能實現我們想要的效果。如下實現。
fun executeZip() { Observable.zip(getGoodsObservable(1), getGoodsObservable(2), getGoodsObservable(3), Function3<Goods, Goods, Goods, List<Goods>> { goods0, goods1, goods2 -> val list = ArrayList<Goods>() list.add(goods0) list.add(goods1) list.add(goods2) list }).subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ Log.e(TAG, it.toString()) }, { Log.e(TAG, it.toString()) }) }
既然實現了,那我們執行幾次,發現完美的實現了我們想要的效果,即併發的執行了,也保證了我們請求資料的順序性。
在回撥中運用RxJava
在上面我們的單個網路請求是一個同步的請求,如果我們的網路請求封裝了,線上程中請求,請求成功後在主執行緒中回撥,那我們又該如何建立呢使用呢?
先來模擬一個子執行緒請求網路,請求成功回撥資料給主執行緒。
fun getGoods(ctx:Context,id: Int,callbacks:(goods:Goods)->Unit): Unit { ctx.doAsync { Thread.sleep(Random().nextInt(1000).toLong()) var data = Goods(id, Random().nextInt(20), "地址${id}") ctx.runOnUiThread { callbacks(data) } } }
getGoods傳了三個引數,第一個Context物件,第二個是商品ID,第三個引數是一個函式,(goods:Goods)->Unit表示第三個引數的型別是一個引數為Goods型別並且返回Unit的函式。使用doAsync 模擬非同步請求,請求成功後runOnUiThread 切換到UI執行緒。然後callbacks(data)將資料回撥。這種使用方式比Java中回撥優美好用太多了。
接下來就開始在回撥成功後建立Observable
fun getGoodsCallBack(id: Int): Observable<Goods> { var subscrbe: ObservableEmitter<Goods>? = null var o = Observable.create<Goods> { subscrbe = it } //Kotlin特性 getGoods(this@MainActivity, id) { subscrbe?.onNext(it) } return o } fun executeZipCallBack() { Observable.zip(getGoodsCallBack(1).subscribeOn(Schedulers.newThread()), getGoodsCallBack(2).subscribeOn(Schedulers.newThread()), getGoodsCallBack(3).subscribeOn(Schedulers.newThread()), Function3<Goods, Goods, Goods, List<Goods>> { goods0, goods1, goods2 -> val list = ArrayList<Goods>() list.add(goods0) list.add(goods1) list.add(goods2) list }).subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ Log.e(TAG, it.toString()) }, { Log.e(TAG, it.toString()) }) }
ok,到這裡回撥情況下建立使用RxJava也介紹完畢,到此本篇文章就結束了,有問題歡迎指出,內容雜亂,多多擔待,Hava a wonderful day.
相關文章
- 【譯】當 Kotlin 遇見 GradleKotlinGradle
- 使用RxJava從多個資料來源獲取資料RxJava
- 使用 RxJava 從多種來源中載入資料RxJava
- 當Elasticsearch遇見KafkaElasticsearchKafka
- 多資料來源配置
- 航空遇見大資料大資料
- 當 Node.js 遇見 DockerNode.jsDocker
- 當Node.js遇見DockerNode.jsDocker
- 當 better-scroll 遇見 VueVue
- MyBatis配置多資料來源MyBatis
- web 配置多資料來源Web
- 多資料來源與動態資料來源的權衡
- ODBC 常見資料來源配置整理
- 當ArcGIS API for JavaScript遇見Webpack(二)APIJavaScriptWeb
- Spring多資料來源配置Spring
- SpringBoot多資料來源Spring Boot
- Spring配置多資料來源Spring
- 資料庫排行榜|當 DB-Engines 遇見墨天輪國產資料庫排行資料庫
- 當Elasticsearch遇見智慧客服機器人Elasticsearch機器人
- 當 dbt 遇見 TiDB丨高效的資料轉換工具讓資料分析更簡單TiDB
- Spring多資料來源獲取Spring
- 實現多資料來源事務
- Spring Boot 配置多資料來源Spring Boot
- Spring Boot 多資料來源配置Spring Boot
- 多個資料來源的問題
- springboot多資料來源配置Spring Boot
- springBoot 多資料來源配置Spring Boot
- SpringBoot配置多資料來源Spring Boot
- Spring-Boot 多資料來源配置+動態資料來源切換+多資料來源事物配置實現主從資料庫儲存分離Springboot資料庫
- [原始碼解析] 當 Java Stream 遇見 Flink原始碼Java
- 當深度學習遇見自動文字摘要深度學習
- 有容雲-PPT | 當微服務遇見容器微服務
- 當知識圖譜“遇見”深度學習深度學習
- ssm讀寫分離多資料來源SSM
- SpringBoot 配置多資料來源 MyBatisSpring BootMyBatis
- mybatis 多資料來源動態切換MyBatis
- SpirngBoot整合Mybatis Plus多資料來源bootMyBatis
- SpringBoot 的多資料來源配置Spring Boot