[英]How to update a TextView after an API response with Kotlin coroutines?
我對 Kotlin、協程和 API 調用完全陌生,我正在嘗試基於此 API制作一個應用程序。
我的目的是在我的MainActivity
顯示游戲的信息,所以我需要為此目的填充一些TextView
。
我的 API 調用和響應系統運行良好:響應正常且沒有錯誤,但調用是使用 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()
方法就是這里的問題:app 會成功構建,如果在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 開發人員文檔 - “查看 Android Jetpack 的綁定部分。” )
更改您的onCreate
方法如下:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
b = ActivityMainBinding.inflate(layoutInflater)
setContentView(b.root)
listAllGames()
}
另一個答案解釋了您的觀點問題,但您在協程方面也有一些問題。
lifecycleScope
而不是創建新范圍。 如果 Activity 被銷毀,例如在屏幕旋轉期間, lifecycleScope
將自動取消以避免泄漏 Activity。call.await()
掛起函數,而不是直接阻塞線程並且必須指定調度程序。 這也使您可以將事情留在主調度程序上並直接更新 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 元素。 然后您的 Activity 可以觀察變化。 如果調用開始並旋轉屏幕,API 調用將繼續運行並仍將其更改發布到新 Activity 的 UI,而無需重新啟動。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.