如何在baseViewModel上实现coroutine async/等待

发布于 2025-01-22 14:21:13 字数 3880 浏览 2 评论 0 原文

我正在使用此结构与Coroutine一起使用Raterofit来点击我的应用程序中的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等待从GetDataa和GetDatab读取有关我的活动/片段的数据,如何使用该结构来实施该结构,并保持在其他API请求中使用单个请求的能力

I'm using Retrofit with Coroutine using this structure to hit the API requests in my app but at some screens I send multiple request and I want to only show data if the all requests was loaded but I can't do that using this structure?

  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)
                )
            )
    }
}

I want to use aysnc await to read data from getDataA and getDataB on my activity/Fragment how to implement that using this structure and also keep the ability to use single request in other api requests

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

两仪 2025-01-29 14:21:13

要并行调用函数,您可以使用 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
    }
} 

在您当前的结构中,我认为在 withContext(dispatchers.io)在 > persionnetworkCall()方法,除非您要回调 doonsuccess doonfailure 在后台线程中调用。


更新

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

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 函数,它是为工作的并行分解而设计的。当此范围中的任何儿童Coroutine失败时,此范围会失败,其余的孩子都被取消。

To call functions in parallel you can use async builder:

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
    }
} 

In your current structure, I don't think there is a need in calling withContext(Dispatchers.IO) in performNetworkCall() method, unless you want the callbacks doOnSuccess and doOnFailure to be called in background thread.


UPDATE

Try to use next calls to reuse your current structure:

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
}

I used coroutineScope function, it is designed for parallel decomposition of work. When any child coroutine in this scope fails, this scope fails and all the rest of the children are cancelled.

冷默言语 2025-01-29 14:21:13

而不是 dataastatus databstatus 。您可以拥有一个单个实时数据,例如 singleliveEvent&lt; pair&lt;状态,状态&gt;&gt;()

 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
        }
    }

Instead of dataAStatus and dataBStatus. You can have one single live data like 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))
            
        }
    }

Here myApiCalls.getDataA() and myApiCalls.getDataB() should be returning status.

And then observe on this live data (dataStatus) in your activity or fragment.

private fun observeData() {
        viewModel.dataStatus.observe(this){
            val firstData = it.first
            val secondData = it.second
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文