簡體   English   中英

如何在使用 Kotlin 協程的 API 響應后更新 TextView?

[英]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。
  • 您應該使用 Retrofit 的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM