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 dataobserver 可以解决你的问题。

您需要更改如下所示的 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代替launchasync

...
runBlocking(Dispatchers.IO) {
    failed.forEachIndexed { index, f ->    
        println("NOW ${index}")
        callForwardAPI(context, f)
    }
}
...