简体   繁体   English

使用协同程序在进行网络呼叫时更新UI

[英]Use coroutines to update UI while making network call

I am trying to show a spinner while making a network call using coroutines. 我正在尝试使用协同程序进行网络调用时显示一个微调器。 The UI seems to not show the LOADING_ITEMS state (a spinner) until the itemsFromRepo call returns, then the spinner shows for a split second, then the items display. UI似乎没有显示LOADING_ITEMS状态(一个微调器),直到itemsFromRepo调用返回,然后微调器显示一瞬间,然后显示项目。 I was under the impression that as it was in a coroutine, the state would be set to LOADING_ITEMS, the items would be cleared, the network call would be made in the background while the spinner is showing on the UI. 我的印象是,在协程中,状态将被设置为LOADING_ITEMS,项目将被清除,当微调器在UI上显示时,网络调用将在后台进行。 Then when the network call has finished, the coroutine would continue running and set the items then state. 然后,当网络调用完成后,协程将继续运行并设置项目然后状态。

Is this the correct way of using Coroutines? 这是使用Coroutines的正确方法吗? And scope, I think that's new from the experimental coroutines I played with months ago. 和范围,我认为这是我几个月前玩过的实验协同程序的新功能。

// ViewModel.kt
enum class State { LOADING_ITEMS, SELECTING_ITEM } 

var state = ObservableField<State>()   
var items = ObservableField<List<String>>()    

private fun loadItems() {
    state.set(State.LOADING_ITEMS)
    items.set(emptyList())
    GlobalScope.launch(Dispatchers.Main) {
        val itemsFromRepo = apiRepo.getItems() // a network call
        items.set(itemsFromRepo)
        state.set(State.SELECTING_ITEM)
    }
}


// Repo.kt
suspend fun getItems() = suspendCoroutine<List<String>> { cont ->
    FirebaseDatabase.getInstance().getReference("Items")
            .addListenerForSingleValueEvent(
            object : ValueEventListener {
                override fun onCancelled(error: DatabaseError?) {
                    cont.resume(listOf(error?.message ?: "Unknown error"))
                }

                override fun onDataChange(snap: DataSnapshot?) {
                    cont.resume(snap?.children?.map { it.key } ?: emptyList())
                }
            })
}

The best practice is to use local scope to handle coroutines: 最佳实践是使用本地范围来处理协同程序:

class ViewModel : CoroutineScope {
    private var job: Job = Job()

    // To use Dispatchers.Main (CoroutineDispatcher - runs and schedules coroutines) in Android add
    // implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1'
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job

    enum class State { LOADING_ITEMS, SELECTING_ITEM } 

    var state = ObservableField<State>()   
    var items = ObservableField<List<String>>()


    fun detachView() {
        job.cancel()
    }

    private fun loadItems() {
        state.set(State.LOADING_ITEMS)
        items.set(emptyList())
        launch {
            val itemsFromRepo = apiRepo.getItems()
            items.set(itemsFromRepo)
            state.set(State.SELECTING_ITEM)
        }
    }
}

And regarding your question: 关于你的问题:

Is this the correct way of using Coroutines? 这是使用Coroutines的正确方法吗?

Yes it is correct way. 是的,这是正确的方法。 If you have network call inside a suspend function (that is in your case), then this function will suspend coroutine execution until you call continuation.resume() or other related methods to resume coroutine. 如果你在suspend函数(在你的情况下)中有网络调用,那么这个函数将暂停协程执行,直到你调用continuation.resume()或其他相关方法来恢复协程。 And suspending the coroutine will not block the main thread. 并且挂起协程不会阻止main线程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM