现在有一个需求:股票行情报价跟图表数据是由两个接口返回,产品想同一时间展示两个数据,另外五秒轮训一次接口,报错重试三次。
我们分别用协程,RxJava2和Flow分别处理一下数据处理的部分。
协程
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() {
viewModelScope.launch(Dispatchers.IO) {
try {
val tickerInfoDeferred = async { model.loadData() } // 行情报价
val chartInfoDeferred = async { chartModel.loadData() } // 图表接口
val tickerInfoData = tickerInfoDeferred.await()
val chartInfoData = chartInfoDeferred.await()
viewModelList.clear()
// 行情数据和图表数据合并
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
)
})
// 主线程
mutableLiveData.postValue(ChartUIEvent.ShowTickerData(viewModelList, false))
// 执行五秒轮询一次的逻辑
runLoop()
} catch (e: Throwable) {
e.printStackTrace()
// 错误重试
if (retryCount < 3) {
loadData()
retryCount++
}
}
}
}
private var loopTime: Long = 0L
private fun runLoop() {
if (System.currentTimeMillis() - loopTime < NineChartPresenter.DELAY_TIME) {
return
}
viewModelScope.launch {
delay(NineChartPresenter.DELAY_TIME)
loadData()
}
loopTime = System.currentTimeMillis()
}
}
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秒轮训
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)
}, { })
)
}
}
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(): 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()
}
}
总结
对比上面三种实现方式,可以看到RxJava2
和Flow
的处理代码量更少,更加易读易懂,不需要额外再去try catch
异常。
RxJava2
对比Flow
,Flow
和LiveData
通过liveData
结构体的的方式结合更加简单自然,没有RxJava2
那么生硬。另外Rxjava2
需要通过CompositeDisposable
来取消订阅事件,避免内存泄漏,而Flow
不需要担心内存泄漏的问题,当Activity
销毁的时候会自动cancel
掉liveData
所在的协程,从而取消Flow
生产者。
所以,我建议正确的架构应该是在 View
和 ViewModel
间使用 LiveData
进行通讯,并在应用的底层和 DataRepository
层架构中使用协程挂起函数或者Flow
,业务简单的话直接使用挂起函数处理即可,业务复杂的话使用Flow