![](/img/trans.png)
[英]How to write a coroutine function in BaseViewModel that can be used in child ViewModel
[英]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)
,除非您希望在后台线程中调用回调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
function,它是为并行分解工作而设计的。 当这个 scope 中的任何一个子协程失败时,这个 scope 失败,所有的子协程 rest 被取消。
而不是dataAStatus
和dataBStatus
。 您可以拥有一个单一的实时数据,例如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.