Question: How to properly wrap RxJava 2 functions with suspend functions
See original GitHub issueLet’s say I have the following RxJava-based API client class:
class RxJavaBasedApi {
fun getUser(): Single<User> {
// Underlying implementation details may be Retrofit + RxJava 2
}
}
Now I wish to wrap this in a suspend
function like so:
class UserApiClient(private val rxJavaBasedApi) {
suspend fun getUser(): User {
return rxJavaBasedApi.getUser().await()
}
}
Then in an Android ViewModel
, let’s say I have the following:
class UserViewModel(
private val userApiClient: UserApiClient,
private val dispatchers: Dispatchers // Wrapper Dispatchers instance for DI purposes
) : ViewModel(),
CoroutineScope {
override val coroutineContext: CoroutineContext = Job() + dispatchers.main
init {
launch {
val user = userApiClient.getUser() // NetworkOnMainThreadException
}
}
...
}
When getUser()
gets called, the underlying Single
will get subscribed which by default operates in a synchronous, blocking manner since there was no subscribeOn()
/observerOn()
specified downstream.
Although Single.await()
is a suspend
function, in this particular case, it will block on the current thread in which it was invoked.
Questions:
- Should the
ViewModel
take on the responsibility of having to wrap the call withwithContext(dispatchers.io)
in theinit
block? - Should
UserApiClient
instead take care of the implementation details of wrapping the call withinwithContext(dispatchers.io)
? This avoids having to specifywithContext
in theViewModel
. If so:- Should the
Single.await()
extension still be used? Does it provide any value withinwithContext
? - Should
Single.blockingGet()
be used instead? It would be a non-suspend
invocation withinwithContext
.
- Should the
Issue Analytics
- State:
- Created 5 years ago
- Comments:6 (3 by maintainers)
Top Results From Across the Web
How to create a safe call wrapper for RxJava functions in Kotlin?
You can easily make this work with RxJava by converting your observables to suspending functions using the awaitFirst() or await() (for ...
Read more >Kotlin Coroutines by Tutorials, Chapter 4: Suspending Functions
In this chapter, you'll learn more about how suspendable functions work from within. You will see how you can convert existing code, which...
Read more >Coroutines under the hood - Kt. Academy
Here there are two suspending functions: getUserId and getUserName . We also added a parameter token , and our suspending function also returns...
Read more >Are Kotlin Coroutines Enough to Replace RxJava? - JVM Advent
But keep in mind that you don't necessarily need to wrap all your suspend functions in try/catch to handle exceptions.
Read more >Misnomers, Mistakes and Misunderstandings to watch for ...
When designing your methods and functions, start with a suspend function and if you get stuck, change it to a Flow. Such a...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Both are fine, though I’m biased towards
.await
just to make it idiomatic 😃 Don’t you mind if I close the issue?The problem arises from the fact, that you have to control two concurrency levels: one from Rx and one from coroutines. Yes, you can use
subscribeOn
, but if you already opted-in in coroutines-based API (by makinggetUser
suspendable), it is better to usewithContext
.Better not to. If your API is already suspendable and IO-related, it should use
withContext(IO)
on its own. Scheduler and dispatcher are an implementation detail and should be encapsulated in the service itself (UserApiClient
). In that case, it could provide a stable contract (“use me from everywhere”), be easily mocked in tests no matter in what context it is used and its context will be configurable in a single place (UserApiClient
constructor)