LukasLechnerDev / Kotlin-Coroutines-and-Flow-UseCases-on-Android

🎓 Learning Kotlin Coroutines and Flows for Android by example. 🚀 Sample implementations for real-world Android use cases. 🛠 Unit tests included!
Apache License 2.0
2.63k stars 433 forks source link

Main or IO thread #2

Closed w201 closed 2 years ago

w201 commented 4 years ago

According to this code your network request will be made in MainThread. Isn't it?

`class PerformSingleNetworkRequestViewModel( private val mockApi: MockApi = mockApi() ) : BaseViewModel() {

fun performSingleNetworkRequest() {
    uiState.value = UiState.Loading
    viewModelScope.launch {
        try {
            val recentAndroidVersions = mockApi.getRecentAndroidVersions()
            uiState.value = UiState.Success(recentAndroidVersions)
        } catch (exception: Exception) {
            Timber.e(exception)
            uiState.value = UiState.Error("Network Request failed!")
        }
    }
}}`

Maybe need to change it a little bit to launch(Dispatchers.IO) {... and uiState.postValue(...)

LukasLechnerDev commented 4 years ago

Retrofit offers "main-safety" when calling suspend functions like getRecentAndrdoidVersions(). This means that we can call it safely from the main thread and Retrofit switches execution to another thread "internally". More information about main safety in this Google Codelab: https://codelabs.developers.google.com/codelabs/kotlin-coroutines/#4.

Btw. calling Room suspend functions are also main safe.

If you want to dig deeper: As far as I understand, the Coroutine Integration in Retrofit is just a small Wrapper that converts the callback-based API with Callbacks into a coroutine-based API with suspend functions. It does so using the suspendCoroutine{} construct.

If you check out the Release Notes when idiomatic Coroutine support was introduced in v2.6.0, it says "Behind the scenes this behaves as if defined as fun user(...): Call<User> and then invoked with Call.enqueue"

I hope it is clearer now 😀

w201 commented 4 years ago

Big thanks for the clean explanation. I newbie in coroutines after reactive all looks a little bit strange and as magic :)

demoritas commented 4 years ago

Had the same question when looking at the code, thanks for the explanation.

If the simplicity of the coroutines example relies on Retrofit handling part of the complexity in the background that means the example is not necessarily transferable to other asynchronous operations, right?

LukasLechnerDev commented 4 years ago

It depends on the "asynchronous operation".

If you are using a library that exposes suspend functions, you can do it in the same way as it is a convention that suspend functions are non-blocking and you can assume that the library switches to a background thread internally. This is the case for Room & Retrofit.

If you call a synchronous function (that blocks the thread), you have to switch threads yourself with the withContext() construct.

Asynchronous functions in which you have to pass a callback, can be wrapped with the suspendCoroutine{} construct to be able to call it from a coroutine in an idiomatic way.

Does that make sense or do you need more information?