多个 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 个例子:
- 使用 launch 而不 returning 值并直接更新 UI
在协程中,一旦字符串准备就绪。
- 与你的方法类似,你也可以使用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
}
我需要 运行 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 个例子:
- 使用 launch 而不 returning 值并直接更新 UI 在协程中,一旦字符串准备就绪。
- 与你的方法类似,你也可以使用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
}