【问题标题】:Use coroutines to update UI while making network call在进行网络调用时使用协程更新 UI
【发布时间】:2019-01-01 09:39:27
【问题描述】:

我试图在使用协程进行网络调用时显示微调器。 UI 似乎不显示 LOADING_ITEMS 状态(微调器),直到 itemsFromRepo 调用返回,然后微调器显示片刻,然后显示项目。我的印象是,在协程中,状态将设置为 LOADING_ITEMS,项目将被清除,网络调用将在后台进行,同时微调器显示在 UI 上。然后当网络调用完成后,协程将继续运行并设置项目然后状态。

这是使用协程的正确方法吗?还有范围,我认为这是我几个月前玩过的实验性协程的新内容。

// 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())
                }
            })
}

【问题讨论】:

    标签: android kotlin kotlin-coroutines


    【解决方案1】:

    最佳实践是使用本地作用域来处理协程:

    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)
            }
        }
    }
    

    关于你的问题:

    这是使用协程的正确方式吗?

    是的,这是正确的方法。如果您在 suspend 函数内有网络调用(即在您的情况下),那么此函数将暂停协程执行,直到您调用 continuation.resume() 或其他相关方法来恢复协程。并且挂起协程不会阻塞main线程。

    【讨论】:

    • 在不依赖Android的情况下有没有办法做到这一点?我正在研究多平台项目,所以这将是理想的。下面的方法也有效,但是它收到了 2 票反对,所以我很犹豫是否接受它
    • 没有在多平台项目上尝试过。也许这些链接会有所帮助:github.com/Kotlin/kotlinx.coroutines/issues/644github.com/JetBrains/kotlinconf-app。如果您能分享您在多平台项目中使用协程的经验,那就太好了。
    猜你喜欢
    • 2019-07-07
    • 1970-01-01
    • 2019-02-23
    • 2021-01-01
    • 1970-01-01
    • 2013-08-11
    • 2020-09-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多