Kotlin 在进入下一个索引之前等待 for 循环中的协程
Kotlin wait for coroutine in for loop before going onto next index
嗨,我正在使用 for 循环来调用协程方法(调用改造),对于每个循环,我都希望协程方法完成(改造响应后),但我的循环似乎在不等待协程的情况下继续运行完成方法...下面是我的循环方法:-
fun forwardFailedSMS(context: Context) {
var failed = getFailedSms(context)
failed.forEachIndexed { index, f ->
println("NOW ${index}")
GlobalScope.launch(Dispatchers.IO) {
var time = measureTimeMillis {
val fn = async {
callForwardAPI(context, f)
}
val result = fn.await()
}
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//I want the above to finish before going to next index...
}
refreshSmsList(context!!)
}
下面是使用 Retrofit 调用 API 的 callForwardAPI 函数:-
suspend fun callForwardAPI(context: Context,sms: SmsData) {
val databaseHandler: DatabaseHandler = DatabaseHandler(context)
val retrofit = Retrofit.Builder()
.baseUrl("https://backend.mydomain.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(SMSService::class.java)
val api = GlobalScope.async {
val response = retrofit.postForwardSMS(
sms.sender,
sms.message
).awaitResponse()
if (response.isSuccessful) {
println("DONE SUCCESS ${sms.message}")
}
}
api.await()
}
日志“NOW”循环并在特定循环打印“DONE SUCCESS”之前打印...
您必须将 .launch{ }
方法移到 forEach
之外。
目前正在发生的事情是,您的挂起方法将被挂起,但在启动内,因此 for 可以继续。
如果您将启动移到 for 之外,您的 for 循环也会在每个暂停点暂停
您的服务调用包含协程,因此您不需要在 forwardFailedSMS 函数中使用协程。您正在创建两个独立的异步作业。我假设使用 live data 和 observer 可以解决你的问题。
您需要更改如下所示的 forwardFailedSMS 功能:
lateinit var failedListSize: Int
lateinit var serviceCallResponseCount: Int
fun forwardFailedSMS(context: Context) {
var failed = getFailedSms(context)
failedListSize = failed.size
serviceCallResponseCount = 0
failed.forEachIndexed { index, f ->
println("NOW ${index}")
callForwardAPI(context, f)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// I assume that you are using viewmodel and fragment. If you use another pattern, you can change onViewCreate to another lifecycle function.
viewModel.myLiveData.observe(viewLifeCycleOwner, Observer{
println("NOW ${index} observed response")
serviceCallResponseCount++
if (serviceCallResponseCount == failedListSize){
refreshSmsList(context!!)
}
}
}
和您的 callForwardAPI 到:
val myLiveData: MutableLiveData<QueryOnlinePolicyResponse> = MutableLiveData()
suspend fun callForwardAPI(context: Context,sms: SmsData) {
val databaseHandler: DatabaseHandler = DatabaseHandler(context)
val retrofit = Retrofit.Builder()
.baseUrl("https://backend.mydomain.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(SMSService::class.java)
val api = GlobalScope.async {
val response = retrofit.postForwardSMS(
sms.sender,
sms.message
).awaitResponse()
// I put postValue here because we are waiting same response count with fail in our observer
myLiveData.postValue(response)
if (response.isSuccessful) {
println("DONE SUCCESS ${sms.message}")
}
}
api.await()
}
我相信这个模式会解决你的问题。我无法尝试,我使用编辑器。如有错误请见谅
如果你想让forwardFailedSMS()
保持常规的阻塞方法,你可以使用runBlocking
代替launch
和async
...
runBlocking(Dispatchers.IO) {
failed.forEachIndexed { index, f ->
println("NOW ${index}")
callForwardAPI(context, f)
}
}
...
嗨,我正在使用 for 循环来调用协程方法(调用改造),对于每个循环,我都希望协程方法完成(改造响应后),但我的循环似乎在不等待协程的情况下继续运行完成方法...下面是我的循环方法:-
fun forwardFailedSMS(context: Context) {
var failed = getFailedSms(context)
failed.forEachIndexed { index, f ->
println("NOW ${index}")
GlobalScope.launch(Dispatchers.IO) {
var time = measureTimeMillis {
val fn = async {
callForwardAPI(context, f)
}
val result = fn.await()
}
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//I want the above to finish before going to next index...
}
refreshSmsList(context!!)
}
下面是使用 Retrofit 调用 API 的 callForwardAPI 函数:-
suspend fun callForwardAPI(context: Context,sms: SmsData) {
val databaseHandler: DatabaseHandler = DatabaseHandler(context)
val retrofit = Retrofit.Builder()
.baseUrl("https://backend.mydomain.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(SMSService::class.java)
val api = GlobalScope.async {
val response = retrofit.postForwardSMS(
sms.sender,
sms.message
).awaitResponse()
if (response.isSuccessful) {
println("DONE SUCCESS ${sms.message}")
}
}
api.await()
}
日志“NOW”循环并在特定循环打印“DONE SUCCESS”之前打印...
您必须将 .launch{ }
方法移到 forEach
之外。
目前正在发生的事情是,您的挂起方法将被挂起,但在启动内,因此 for 可以继续。
如果您将启动移到 for 之外,您的 for 循环也会在每个暂停点暂停
您的服务调用包含协程,因此您不需要在 forwardFailedSMS 函数中使用协程。您正在创建两个独立的异步作业。我假设使用 live data 和 observer 可以解决你的问题。
您需要更改如下所示的 forwardFailedSMS 功能:
lateinit var failedListSize: Int
lateinit var serviceCallResponseCount: Int
fun forwardFailedSMS(context: Context) {
var failed = getFailedSms(context)
failedListSize = failed.size
serviceCallResponseCount = 0
failed.forEachIndexed { index, f ->
println("NOW ${index}")
callForwardAPI(context, f)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// I assume that you are using viewmodel and fragment. If you use another pattern, you can change onViewCreate to another lifecycle function.
viewModel.myLiveData.observe(viewLifeCycleOwner, Observer{
println("NOW ${index} observed response")
serviceCallResponseCount++
if (serviceCallResponseCount == failedListSize){
refreshSmsList(context!!)
}
}
}
和您的 callForwardAPI 到:
val myLiveData: MutableLiveData<QueryOnlinePolicyResponse> = MutableLiveData()
suspend fun callForwardAPI(context: Context,sms: SmsData) {
val databaseHandler: DatabaseHandler = DatabaseHandler(context)
val retrofit = Retrofit.Builder()
.baseUrl("https://backend.mydomain.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(SMSService::class.java)
val api = GlobalScope.async {
val response = retrofit.postForwardSMS(
sms.sender,
sms.message
).awaitResponse()
// I put postValue here because we are waiting same response count with fail in our observer
myLiveData.postValue(response)
if (response.isSuccessful) {
println("DONE SUCCESS ${sms.message}")
}
}
api.await()
}
我相信这个模式会解决你的问题。我无法尝试,我使用编辑器。如有错误请见谅
如果你想让forwardFailedSMS()
保持常规的阻塞方法,你可以使用runBlocking
代替launch
和async
...
runBlocking(Dispatchers.IO) {
failed.forEachIndexed { index, f ->
println("NOW ${index}")
callForwardAPI(context, f)
}
}
...