Now there is a requirement: stock quotation and chart data are returned by two interfaces, the product wants to show two data at the same time, another five seconds of rotation interface, error retry three times.
We use coroutine, RxJava2 and Flow to deal with the data processing part respectively.
coroutines
class NineDemoCoroutineViewModel(private val tickerIdList: List<String>) : ViewModel(),
LifecycleObserver {
private var model: MarketTickerModel = MarketTickerModel(tickerIdList)
private var chartModel: NineChartModel = NineChartModel(tickerIdList)
private val mutableLiveData by lazy { MutableLiveData<ChartUIEvent>() }
val liveData: LiveData<ChartUIEvent> = mutableLiveData
private var viewModelList: MutableList<StockViewModel> =
Collections.synchronizedList(mutableListOf())
private var retryCount: Int = 0
fun loadData(a) {
viewModelScope.launch(Dispatchers.IO) {
try {
val tickerInfoDeferred = async { model.loadData() } // Quotations
val chartInfoDeferred = async { chartModel.loadData() } // Graph interface
val tickerInfoData = tickerInfoDeferred.await()
val chartInfoData = chartInfoDeferred.await()
viewModelList.clear()
// Market data and chart data merge
viewModelList.addAll(tickerInfoData.map {
val tickerInfo = it
val chartInfo = chartInfoData.firstOrNull { tickerInfo.tickerId == it.tickerId }
StockViewModel(
tickerInfo.tickerId,
tickerInfo.type,
tickerInfo.name ?: "--", CountyResManager.getDrawableResByCounty(tickerInfo.regionCode), tickerInfo.close ? :"", tickerInfo.changeRatio ? :"", chartInfo? .entryList, chartInfo? .maxLength ? :0)})/ / main thread
mutableLiveData.postValue(ChartUIEvent.ShowTickerData(viewModelList, false))
// Execute the logic of polling every five seconds
runLoop()
} catch (e: Throwable) {
e.printStackTrace()
// Error retry
if (retryCount < 3) {
loadData()
retryCount++
}
}
}
}
private var loopTime: Long = 0L
private fun runLoop(a) {
if (System.currentTimeMillis() - loopTime < NineChartPresenter.DELAY_TIME) {
return
}
viewModelScope.launch {
delay(NineChartPresenter.DELAY_TIME)
loadData()
}
loopTime = System.currentTimeMillis()
}
}
Copy the code
RxJava2
class NineDemoRxJavaViewModel(private val tickerIdList: List<String>) : ViewModel(),
LifecycleObserver {
companion object {
const val TAG = "NineDemoRxJavaViewModel"
}
private val mutableLiveData by lazy { MutableLiveData<ChartUIEvent>() }
val liveData: LiveData<ChartUIEvent> = mutableLiveData
private var model: MarketTickerModel = MarketTickerModel(tickerIdList)
private var chartModel: NineChartModel = NineChartModel(tickerIdList)
fun loadData(disposable: CompositeDisposable) {
disposable.add(
// 5 seconds rotation
Observable.interval(10, TimeUnit.SECONDS).flatMap {
Observable.zip(
model.loadData2(),
chartModel.loadData2(),
BiFunction<List<TickerBase>, List<ChartViewModel>, List<StockViewModel>> { t1, t2 ->
t1.map {
val tickerBase = it
val chartModel = t2.firstOrNull { it.tickerId == tickerBase.tickerId }
StockViewModel(
tickerBase.tickerId,
tickerBase.type,
tickerBase.name ?: "--", CountyResManager.getDrawableResByCounty(tickerBase.regionCode), tickerBase.close ? :"", tickerBase.changeRatio ? :"", chartModel? .entryList ? : arrayListOf<Entry>(), chartModel? .maxLength ? :0
)
}
})
}.retry(3)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
Log.i(TAG, "loadDataSuccess: ")
mutableLiveData.value = ChartUIEvent.ShowTickerData(it)
}, { })
)
}
}
Copy the code
Flow
class NineDemoFlowViewModel(private val tickerIdList: List<String>) : ViewModel(),
LifecycleObserver {
private var tickerModel: MarketTickerModel = MarketTickerModel(tickerIdList)
private var chartModel: NineChartModel = NineChartModel(tickerIdList)
val liveData = liveData<ChartUIEvent> {
try {
emitSource(flowData())
} catch (e: Throwable) {
e.printStackTrace()
}
}
private suspend fun flowData(a): LiveData<ChartUIEvent> {
return flow {
while (true) {
delay(5000)
emit(1)
}
}.retry(3)
.map {
tickerModel.loadData().zip(chartModel.loadData()) { t1, t2 ->
StockViewModel(
t1.tickerId,
t1.type,
t1.name ?: "--", CountyResManager.getDrawableResByCounty(t1.regionCode), t1.close ? :"", t1.changeRatio ? :"", t2.entryList ? : arrayListOf<Entry>(), t2.maxLength ? :0
)
}
}.map { ChartUIEvent.ShowTickerData(it) }
.catch { Log.e("NineDemoFlowViewModel"."flowData: ${it.message}") }
.onCompletion { Log.i("NineDemoFlowViewModel"."flowData: onCompletion") }
.flowOn(Dispatchers.IO)
.asLiveData()
}
}
Copy the code
conclusion
Compared to the above three implementations, we can see that RxJava2 and Flow process less code, more easy to read, no extra try catch exception.
RxJava2 compares Flow and LiveData. Flow and LiveData are combined in the way of LiveData structure, which is simpler and more natural than RxJava2. In addition, Rxjava2 needs to unsubscribe events through CompositeDisposable to avoid memory leaks, whereas Flow does not need to worry about memory leaks. When the Activity is destroyed, it will automatically cancel the coroutine in which liveData is stored, thus canceling the Flow producer.
Therefore, I suggest that the right architecture should be to use LiveData to communicate between the View and ViewModel, and use coroutine suspension functions or Flow at the bottom of the application and in the DataRepository layer. If business is simple, use suspension functions directly. Flow is used for complex business