多个 API 同时调用并在相应请求的处理完成后立即更新 UI Android

Multiple API calls Simultaneously and update UI as soon as the processing of the corresponding request finishes Android

我需要 运行 6 API 同时调用 并且需要在相应请求完成时为每个更新 UI

目前我正在使用kotlin协程并行执行使用下面的代码

    suspend fun getAllData() : List<String>{
    return withContext(Dispatchers.IO) {

        lateinit var getObject1Task: Deferred<Response<String>>
        lateinit var getObject2Task: Deferred<Response<String>>
        lateinit var getObject3Task: Deferred<Response<String>>

        lateinit var getObject4Task: Deferred<Response<String>>
        lateinit var getObject5Task: Deferred<Response<String>>
        lateinit var getObjec6Task: Deferred<Response<String>>

        launch {
            getObject1Task = dataApiService.getData()
            getObject2Task = dataApiService.getData()
            getObject3Task = dataApiService.getData()
            getObject4Task = dataApiService.getData()
            getObject5Task = dataApiService.getData()
            getObject6Task = dataApiService.getData()
        }

          var stringList = ArrayList<String >()

        stringList.add(getObject1Task.await().body()!!) /// add All to the list
        stringList
    }
}

我无法找到一种方法来在 API 完成后立即获取每个字符串的数据。 我也尝试了 LiveData 但有些意义不大。

每个字符串之间没有 link,因此 不必将所有字符串添加到列表中

如果 API 彼此不相关,则您为每个 API 启动一个新协程。协程是轻量级的,因此您可以随心所欲地启动。

因为你有 6 个 API,一次启动 6 个协程并在相应的协程中处理它们的响应。

并且您还想遵循 MVVM,因此您可以使用 LiveData 将数据从 viewModel 传递到您的片段或 activity。


在 ViewModel 中,它看起来像这样

// liveDatas for passing data to your fragment or activity
// each liveData should be observed and update their respective views
val object1LiveData = MutableLiveData<String>()
val object2LiveData = MutableLiveData<String>()
val object3LiveData = MutableLiveData<String>()
val object4LiveData = MutableLiveData<String>()
val object5LiveData = MutableLiveData<String>()
val object6LiveData = MutableLiveData<String>()

private val listOfLiveData = listOf(
    object1LiveData,
    object2LiveData,
    object3LiveData,
    object4LiveData,
    object5LiveData,
    object6LiveData
)

fun fetchData(){
    listOfLiveData.forEach { liveData->
        // launching a new coroutine for every liveData
        // for parellel execution
        viewModelScope.launch{
            callApiAndUpdateLiveData(liveData)
        }
    }  
}

private suspend fun callApiAndUpdateLiveData(liveData: MutableLiveData<String>){
    val response = dataApiService.getData().await()
    liveData.value = response.body()!!
}

使用协同程序,有多种方法可以实现这一点。这里有 2 个例子:

  1. 使用 launch 而不 returning 值并直接更新 UI 在协程中,一旦字符串准备就绪。
  2. 与你的方法类似,你也可以使用async,等待 未来响应 return 值然后更新 UI.

示例1:即发即忘,当每个元素就绪时直接更新UI

从协程中更新 UI 元素时,您应该使用 Dispatchers.Main 作为协程上下文。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)


    repeat(6){index ->
        val id = resources.getIdentifier("tv${index+1}", "id", packageName)
        val textView = findViewById<TextView>(id)
        textViews.add(textView)
    }

    repeat(6){ index ->
        GlobalScope.launch(Dispatchers.Main) { // launch coroutine in the main thread
            val apiResponseTime = Random.nextInt(1000, 10000)
            delay(apiResponseTime.toLong())
            textViews[index].text = apiResponseTime.toString()
        }
    }
}

注意: 在这里,每个 TextView 都会在字符串准备好后立即更新,而不会阻塞主线程。 我在 ID 为 "tv1"、"tv2"...

的 LinearLayout 中使用了 6 个示例 TextView

示例 2:使用并行 async + await(),在所有作业完成后更新 UI(类似于您的作业)

我们在这里并行启动 6 个异步,并在结果准备就绪后立即将其添加到列表中。添加最后一个结果后,我们 return 列表并循环更新 TextView。

val textViews = mutableListOf<TextView>()

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    repeat(6){index ->
        val id = resources.getIdentifier("tv${index+1}", "id", packageName)
        val textView = findViewById<TextView>(id)
        textViews.add(textView)
    }
    // note: again we use Dispatchers.Main context to update UI
    GlobalScope.launch(Dispatchers.Main) {
        val strings = updateUIElementAfterThisFinishes()
        repeat(6){index ->
            textViews[index].text = strings[index]
        }
    }

}

 // for API calls we use Dispatchers.IO context, this function will finish at 10 seconds or less
suspend fun updateUIElementAfterThisFinishes(): List<String> = withContext(Dispatchers.IO){
    val strings = mutableListOf<String>()
    val jobs = Array(6){GlobalScope.async {
        val apiResponseTime = Random.nextInt(1000, 10000)
        delay(apiResponseTime.toLong())
        apiResponseTime.toString()
    }}
    jobs.forEach {
        strings.add(it.await())
    }
    return@withContext strings
}