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