Search code examples
androidkotlinviewmodelkotlin-coroutinescoroutine

In Activity/Fragment, How to get/wait the return value from ViewModel coroutines operation?


Following the codelab demo from Google (link), I try to refactor my code to ViewModel + coroutines. My question is, instead of just insert the data (original code), I want to wait for the result from the inserting operation, which should return the id if the inserting succeeded, then do something based on the result. So how to do it?

Currently, I send a method to the ViewModel insert method as a callback. Of course, observing the ViewModel is another option. But is there any better solution?

My current code:

EventActivity:

viewModel.insert(Event("name"), {
    if (it == -1L) {
        Log.i("insert", "failure")
    } else {
        Log.i("insert", "success: $it")
    }
})

EventViewModel:

private val mEventDao: EventDao = AppDatabase.getDatabase(application).eventDao()
private val mJob = Job()
private val mScope = CoroutineScope(Dispatchers.Main + mJob)

fun insert(event: Event, callback: (id: Long) -> Unit) {
    mScope.launch(Dispatchers.IO) {
        val result =
            try {
                // just for testing delay situation
                delay(5000)
                val id = mEventDao.insertEvent(event)
                id
            } catch (e: Exception) {
                -1L
            }
        withContext(Dispatchers.Main) {
            callback(result)
        }
    }
}

EventDao:

@Dao
interface EventDao {
    fun insertEvent(event: Event): Long
}

Solution

  • You can add a LiveData object to EventViewModel, update it when inserting is done and subscribe to it in Activity:

    class EventViewModel : ViewModel() {
        //...
        var insertionId = MutableLiveData<Long>()
    
        fun insert(event: String) {
            mScope.launch(Dispatchers.IO) {
                val result =
                        try {
                            // just for testing delay situation
                            delay(5000)
                            val id = mEventDao.insertEvent(event)
                            id
                        } catch (e: Exception) {
                            -1L
                        }
    
                insertionId.postValue(result)
            }
        }
    }
    

    And subscribe in the EventActivity:

    class EventActivity : AppCompatActivity() {
    
        lateinit var viewModel: EventViewModel
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            viewModel = ViewModelProviders.of(this).get(EventViewModel::class.java)
            viewModel.insertionId.observe(this, android.arch.lifecycle.Observer { id ->
                // Use `id` for example to update UI. 
            })
    
            // ...
    
            viewModel.insert(Event("name"))
        }
    }