使用协程,有多种方法可以实现这一点。这里有 2 个例子:
- Use launch不返回值并直接更新 UI
一旦字符串准备好,就在协程内。
- 与您的方法类似,您也可以使用async, 等待
未来响应返回值然后更新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 就会更新,而不会阻塞主线程。
我在 LinearLayout 中使用了 6 个示例 TextView,ID 为“tv1”、“tv2”...
示例 2:使用并行 async + wait(),在所有作业完成时更新 UI(与您的类似)
在这里,我们并行启动 6 个异步,并在结果准备好后立即将其添加到列表中。添加最后一个结果后,我们返回列表并循环更新 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
}