繁体   English   中英

如何在 BaseViewModel 上实现协程异步/等待

[英]how to implement Coroutine async/await on BaseViewModel

我将 Retrofit 与协程一起使用,使用此结构在我的应用程序中发送 API 请求,但在某些屏幕上我发送了多个请求,我只想在所有请求都已加载时显示数据,但我不能使用此结构来做到这一点?

  interface MyApi{
        @GET(API_DATA)
        suspend fun getDataA(): Response<BaseResponse<MyApiResponse>>
    
        @GET(API_DATA)
        suspend fun getDataB(): Response<BaseResponse<MyApiResponse>>

       @GET(API_DATA)
        suspend fun getDataC(): Response<BaseResponse<MyApiResponse>>
    }

//

 class MyApiCalls(private val myApi: MyApi) {
     suspend fun getDataA() =
            myApi.getDataA()
    
     suspend fun getDataB() =
            myApi.getDataB()

     suspend fun getDataC() =
            myApi.getDataC()
    }

//

class MyRepository(
    private val myApiCalls: MyApiCalls
    ) : BaseRepository()  {
        suspend fun getDataA() = myApiCalls.getDataA()
        suspend fun getDataB() = myApiCalls.getDataB()
        suspend fun getDataC() = myApiCalls.getDataC()
}

//

class MyViewModel(
    private val repository: MyRepository,
) : BaseViewModel(repository) {
    val dataAStatus = SingleLiveEvent<Status>()
    val dataBStatus = SingleLiveEvent<Status>()
    val dataCStatus = SingleLiveEvent<Status>()
    fun getDataA() {
        performNetworkCall({
            repository.getDataA()
        }, dataAStatus )

    }

    fun getDataB() {
        performNetworkCall({
            repository.getDataB()
        }, dataBStatus)

    }

  fun getDataC() {
        performNetworkCall({
            repository.getDataC()
        }, dataCStatus)

    }

}

//

abstract class BaseViewModel(private val repository: BaseRepository) : ViewModel() {
    val showNetworkError = SingleLiveEvent<Boolean>()
    val statusToken = SingleLiveEvent<Status>()
    val logoutStatus = SingleLiveEvent<Status>()
    val refreshTokenStatus = SingleLiveEvent<Status>()

    fun <D> performNetworkCall(
        apiCall: suspend () -> Response<D>,
        status: SingleLiveEvent<Status>,
        doOnSuccess: (responseData: D?) -> Unit = {},
        doOnFailure: (() -> Any) = {}
    ) {
        if (isNetworkConnected()) {
            viewModelScope.launch {
                withContext(Dispatchers.IO) {
                    try {
                        status.postValue(Status.Loading)
                        val response = apiCall.invoke()
                        when {
                     
                            response.code() in 200..300 -> {
                                doOnSuccess(response.body())
                                status.postValue(Status.Success(response.body()))
                            }
                     
                            else -> {
                                doOnFailure()
                                status.postValue(Status.Error(
                    errorCode = ERRORS.DEFAULT_ERROR,
                    message = repository.getString(R.string.default_error)
                )
            )
                
                    } catch (e: Exception) {
                        doOnFailure()
                       status.postValue(
                Status.Error(
                    errorCode = ERRORS.DEFAULT_ERROR,
                    message = repository.getString(R.string.default_error)
                )
            )
                    }
                }
            }
        } else
            status.postValue(
                Status.Error(
                    errorCode = ERRORS.NO_INTRERNET,
                    message = repository.getString(R.string.no_internet_connection)
                )
            )
    }
}

我想使用 aysnc await 从我的活动/片段上的 getDataA 和 getDataB 读取数据如何使用此结构实现它,并保持在其他 api 请求中使用单个请求的能力

要并行调用函数,您可以使用async构建器:

fun performRequests() {
    viewModelScope.launch {
        val request1 = async { repository.getDataA() }
        val request2 = async { repository.getDataB() }
        val request3 = async { repository.getDataC() }
         // all three requests run in parallel
        
        val response1 = request1.await()
        val response2 = request2.await()
        val response3 = request3.await()

        //... use those responses to get data, notify UI
    }
} 

在您当前的结构中,我认为没有必要在performNetworkCall()方法中调用withContext(Dispatchers.IO) ,除非您希望在后台线程中调用回调doOnSuccessdoOnFailure


更新

尝试使用下一次调用来重用您当前的结构:

performNetworkCall({performRequests()}, status)

suspend fun performRequests(): Response<...> = coroutineScope {
    val request1 = async { repository.getDataA() }
    val request2 = async { repository.getDataB() }
    val request3 = async { repository.getDataC() }
    // all three requests run in parallel

    val response1 = request1.await()
    val response2 = request2.await()
    val response3 = request3.await()

    //... use those responses to compose a general Response object
    val generalResponse: Response<...> = "..."

    generalResponse
}

我使用了coroutineScope function,它是为并行分解工作而设计的。 当这个 scope 中的任何一个子协程失败时,这个 scope 失败,所有的子协程 rest 被取消。

而不是dataAStatusdataBStatus 您可以拥有一个单一的实时数据,例如SingleLiveEvent<Pair<Status,Status>>()

 val dataStatus = SingleLiveEvent<Pair<Status,Status>()
    private fun getAllData(){
        viewModelScope.launch {
            val dataFromA = myApiCalls.getDataA()
            val dataFromB = myApiCalls.getDataB()
            dataStatus.postValue(Pair(dataFromA,dataFromB))
            
        }
    }

这里myApiCalls.getDataA()myApiCalls.getDataB()应该返回status

然后观察您的活动或片段中的实时数据 ( dataStatus )。

private fun observeData() {
        viewModel.dataStatus.observe(this){
            val firstData = it.first
            val secondData = it.second
        }
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM