如何更新一个TextView与Kotlin协程API响应后?



我对Kotlin,协程和API调用完全陌生,我正试图基于此API制作一个应用程序。

我的目的是在我的MainActivity中显示游戏的信息,所以我需要填充一些TextView来达到这个目的。

我的API调用和响应系统工作得很好:响应是OK的,没有错误,但是调用是使用Kotlin的协程,这将不让我更新我的UI后得到响应。

为了简单起见,我只附加了MainActivity代码,这是问题的根源。

class MainActivity : AppCompatActivity() {
private lateinit var b: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
b = ActivityMainBinding.inflate(layoutInflater)
setContentView(R.layout.activity_main)
listAllGames()
}
private fun listAllGames() {
CoroutineScope(Dispatchers.IO).launch {
val call = getRetrofit().create(APIService::class.java).listAllGames("")
val games = call.body()
runOnUiThread {
if (call.isSuccessful) {
b.gameTitle.text = games?.get(0)?.title ?: "Dummy"
b.gameDesc.text = games?.get(0)?.short_description ?: "Dummy"
b.gameGenre.text = games?.get(0)?.genre ?: "Dummy"
}
else {
Toast.makeText(applicationContext, "ERROR", Toast.LENGTH_SHORT).show()
Log.d("mydebug", "call unsuccessful")
}
}
}
}
private fun getRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://www.freetogame.com/api/games/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
}

具体来说,listAllGames()方法是这里的问题:应用程序将成功构建,如果在if (call.isSuccessful)块内添加一个断点,它将正确显示数据;但是当运行应用程序时,显示将永远保持空白。

提前感谢大家!

为了使用视图绑定,您需要将膨胀的视图从绑定传递给setContentView方法。否则,您将使用绑定膨胀视图,但显示没有绑定的XML布局。

查看这里的文档:

private lateinit var binding: ResultProfileBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ResultProfileBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
}

(来源:Android Developer文档- "Android Jetpack的View Binding部分。")

改变你的onCreate方法如下:

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
b = ActivityMainBinding.inflate(layoutInflater)
setContentView(b.root)
listAllGames()
}

另一个答案解释了你的视图的问题,但是你在协程上也有一些问题。

  • 您应该使用lifecycleScope而不是创建一个新的作用域。lifecycleScope将自动取消自己,以避免泄漏活动,如果活动被破坏,如在屏幕旋转。
  • 你应该使用Retrofit的call.await()suspend函数,而不是直接阻塞线程,并且必须指定一个调度程序。这也可以让你把事情留在主调度程序上,直接更新UI,而不必使用runOnUiThread
private fun listAllGames() {
lifecycleScope.launch {
val call = getRetrofit().create(APIService::class.java).listAllGames("")
try { 
val games = call.await()
b.gameTitle.text = games?.get(0)?.title ?: "Dummy"
b.gameDesc.text = games?.get(0)?.short_description ?: "Dummy"
b.gameGenre.text = games?.get(0)?.genre ?: "Dummy"
} catch (e: Exception) {
Toast.makeText(applicationContext, "ERROR", Toast.LENGTH_SHORT).show()
Log.d("mydebug", "call unsuccessful", e)
}
}
}

实际上,你应该把这样的API调用移动到ViewModel中,这样它们可以在屏幕旋转时继续运行。如果这样做,ViewModel中的函数应该更新LiveData或SharedFlow,而不是UI元素。然后你的活动可以观察变化。如果调用被启动并且屏幕被旋转,API调用将继续运行并且仍然将其更改发布到新Activity的UI而无需重新启动。

最新更新