简体   繁体   中英

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. 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. 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? 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?

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. And suspending the coroutine will not block the main thread.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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