what is the difference between RxJava2CallAdapterFactory.create() & RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())? Which one should prefer?
If you pass Scheduler to RxJava2CallAdapterFactory, it will add subscribeOn(scheduler) to each network request you are making. It is just a convenience method.
Difference from the source code
Both methods returns an instance of synchronous observable, but the create() method has no schedulers to operate on by default and the createWithSchedulers(Scheduler scheduler) as name suggests, operates on the respective scheduler by default which you mention in method, as per your example it will be Scheduler.io().
Which one you should use?
Now this will depend on you, the APIs you directly want to return instance in Schedulers.io() or other Schedulers by default then you use the one with schedulers, else you can use the create() method and can declare schedulers while getting response every time.
Note: I personally use create() method as I sometimes mention some other Schedulers.newThread() and/or Schedulers.computation()
Related
Recently I saw this - Most data sources already provide main-safe APIs like the suspend method calls provided by Room or Retrofit. Your repository can take advantage of these APIs when they are available.
What does this mean? Is the dispatcher under the hood Dispatcher.IO for Retrofit and Room? Or do I need to mention that explicitly, while making the request? Thank you.
withContext(Dispatchers.IO) {
// Some retrofit call or room query
}
No you don't need to mention dispatchers for Retrofit and Room. For Room when you mark a dao function as suspend fun it is guaranteed that it will not block main thread.
You can read this article https://medium.com/androiddevelopers/room-coroutines-422b786dc4c5
from the article
Room calls the CoroutinesRoom.execute suspend function, which switches to a background dispatcher, depending on whether the database is opened and we are in a transaction or not.
No, you don't need to switch context when calling suspend functions of Retrofit and Room. I'm not sure if they use Dispatcher.IO under the hood, maybe they use their custom context composed of thread pools, but it is guaranteed to be called in background thread.
For example you can call suspend Dao functions in ViewModel class like the following:
viewModelScope.launch {
val user dao.getCurrentUser()
// Update UI using user
}
assuming getCurrentUser() is a suspend function:
suspend fun getCurrentUser(): User
Marking your Retrofit HTTP request methods and Room DAO query methods as suspend tells both respective libraries to do the asynchronous work for you, meaning that you don't have to explicitly change threads with Dispatchers.IO at all.
Furthermore, even when a Room DAO method isn't marked suspend, but it returns a value wrapped in an Observable such as Kotlin's Flow, or RxJava's Flowable, or Jetpack's LiveData, Room will then run those queries asynchronously for you as well. As per the documentation.
That being said, you should still launch coroutines in that case, whenever you call your async, non-blocking methods with lifecycleScope or viewModelScope depending on where you're calling them from (Activity/Fragment or ViewModel) to harness the full power of suspending functions. lifecycleScope and viewModelScope use Dispatchers.Main.immediate by default, as already stated, you will not need to change Dispatchers.
How can I get the value of a Flow outside a coroutine similarly to LiveData?
// Suspend function 'first' should be called only from a coroutine or another suspend function
flowOf(1).first()
// value is null
flowOf(1).asLiveData().value
// works
MutableLiveData(1).value
Context
I'm avoiding LiveData in the repository layer in favor of Flow. Yet, I need to set, observe and collect the value for immediate consumption. The later is useful for authentication purpose in a OkHttp3 Interceptor.
You can do this
val flowValue: SomeType
runBlocking(Dispatchers.IO) {
flowValue = myFlow.first()
}
Yes its not exactly what Flow was made for.
But its not always possible to make everything asynchronous and for that matter it may not even always be possible to 'just make a synchronous method'. For instance the current Datastore releases (that are supposed to replace shared preferences on Android) do only expose Flow and nothing else. Which means that you will very easiely get into such a situation, given that none of the Lifecycle methods of Activities or Fragments are coroutines.
If you can help it you should always call coroutines from suspend functions and avoid making runBlocking calls. A lot of the time it works like this. But it´s not a surefire way that works all the time. You can introduce deadlocks with runBlocking.
Well... what you're looking for isn't really what Flow is for. Flow is just a stream. It is not a value holder, so there is nothing for you retrieve.
So, there are two major avenues to go down, depending on what your interceptor needs.
Perhaps your interceptor can live without the data from the repository. IOW, you'll use the data if it exists, but otherwise the interceptor can continue along. In that case, you can have your repository emit a stream but also maintain a "current value" cache that your interceptor can use. That could be via:
BroadcastChannel
LiveData
a simple property in the repository that you update internally and expose as a val
If your interceptor needs the data, though, then none of those will work directly, because they will all result in the interceptor getting null if the data is not yet ready. What you would need is a call that can block, but perhaps evaluates quickly if the data is ready via some form of cache. The details of that will vary a lot based on the implementation of the repository and what is supplying the Flow in the first place.
You could use something like this:
fun <T> SharedFlow<T>.getValueBlockedOrNull(): T? {
var value: T?
runBlocking(Dispatchers.Default) {
value = when (this#getValueBlockedOrNull.replayCache.isEmpty()) {
true -> null
else -> this#getValueBlockedOrNull.firstOrNull()
}
}
return value
}
You can use MutableStateFlow and MutableSharedFlow for emitting the data from coroutine and receiving the data inside Activity/Fragment. MutableStateFlow can be used for state management. It requires default value when initialised. Whereas MutableSharedFlow does not need any default value.
But, if you don't want to receive stream of data, (i.e) your API call sends data only once, you can use suspend function inside coroutine scope and the function will perform the task and return the result like synchronous function call.
To get the value of a Flow outside of a coroutine, the best option is to create the flow as a StateFlow and then call the value property on the StateFlow.
class MyClass {
private val mutableProperty = MutableStateFlow(1)
val property = mutableProperty.asStateFlow()
...
mutableProperty.value = 2
}
...
val readProperty = MyClass().property.value
val propertyAsFlow = MyClass().property as Flow<Int>
So there is a new builder function for LiveData which is:
val someLiveData = liveData {
// do something
}
Can anyone explain exactly what this new builder function solve? How does it solve issues on rotation? How does it relate to webservice calls?
Any inputs would be appreciated. Thanks in advance.
Can anyone explain exactly what this new builder function solve?
The current documentation on liveData { } it is pretty good and gives many examples. Here are some benefits you get for free by using it:
Automatic support for timeout and canceling through the optional timeoutInMs (which defaults to 5 seconds).
No need to explicitly launch a coroutine from an init { } block to initialize a MutableLiveData<T> (this hypothetical coroutine is referred to as C below).
No need to worry about what scope to launch C in
No need to maintain code to wait with launching C until it is actually needed (i.e. the LiveData has any registered and active observers).
No need to write code for relaunching C when the LiveData is re-activated.
How does it solve issues on rotation?
LiveData in itself does not solve any issue with preserving state across e.g. screen rotation. That's what ViewModel is for. Typically you have LiveData properties in your ViewModel. But there is not direct relationship between screen rotation problems and liveData { }
How does it relate to webservice calls?
Since the block you pass to liveData { } is a suspend function, you can use coroutine support in your webservice. For example, Retrofit 2.6.0 and later supports suspend modifiers in its HTTP request function definitions, which makes it very convenient to use in the liveData { } code block.
I want to make API calls using Retrofit2 library, returns generic type observable.
I am getting an error: android.os.NetworkOnMainThreadException, while making calls.
Looks really easy to solve, two cases to consider. :1)If you are not using RXJava or 2) if you are using it
1) If you are NOT using RXJava
You should use the method enqueue when you make the call. The error you get is because you are calling the response on the same Thread(the MainThread)
Here is an example took from the web that uses Enqueue with Kotlin that possibly you can adapt to your case
override fun loadPokemonList(pokemonListListener: PokemonListListener) {
call = pokemonService.getPokedex();
call.enqueue(object : Callback<PokeDex> {
override fun onResponse(call: Call<PokeDex>?, response: Response<PokeDex>?) {
if (response != null && response.isSuccessful) {
pokemonListListener.onSuccess(response.body())
} else {
pokemonListListener.onFailure(appContext.getString(R.string.error_fetching_data))
}
}
override fun onFailure(call: Call<PokeDex>?, t: Throwable?) {
pokemonListListener.onFailure(appContext.getString(R.string.error_fetching_data))
}
})
}
2) If you are using RXJava(usually version 2, be aware you will find several tutorials for the first version online)
But please consider that most of the developers today solve this asynchronous call using RXJava2 that is the second case I was mentioning at the beginning of he post. It will take you several hours to understand the basics of it but once you will do, you will have a great skill to bring with you and manage this kind of calls( even multiple ones) will be really simple. In fact RXJava allows you really easy to direct the operation in a concurrent Thread and observe the result on the Main Thread. Here a good tutorial that explains how to do that
with the easy and well known pattern (see the notations)
Observable<List<TrendsResponse>>
.subscribeOn(Schedulers.io())//Run the call on another Thread
.observeOn(AndroidSchedulers.mainThread())//observe on the Main Thread
.subscribe();
Differently by what people new to RXJava think:
on default RXJava does not call a concurrent Thread, is up to you to do that with the subcribeOn that does all the "dirty" work and then to observe the result on the Main Thread with ObserveOn.
For principiants is easy to grasp that comparing both of them to the two famous methods of AsyncTask: doInBackground and onPostExecute
EDIT: Please look also this post if you or future users have problems.
With Retrofit/RxJava, the network call will be default be performed on the thread that subscribes to the Observable returned from the stub. On Android, we are normally executing on the "main" thread, and it is not permitted to access the network on this thread, hence the error.
The solution is to tell RxJava to subscribe to the Observable on a different thread:
getDataFromAPI(/*...*/)
.subscribeOn(Schedulers.io())
.observerOn(AndroidSchedulers.mainThread())
.subscribe({ /* result available in `it` */ })
Schedulers.io() is a reference to a scheduler that uses a set of threads specifically intended to be used for IO operations.
The .observeOn allows you to handle the result safely back on the main thread.
There is conflicting information about when and whether to use subscribeOn with Retrofit.
Here is an answer saying to not use subscribeOn.
Here is an answer seeming to imply that subscribeOn has no good default set.
Here is example code using subscribeOn.
So, once for for all, when should I use subscribeOn and with what thread? What are the possible ramifications of using or not using subscribeOn?
apiService.issueRequest()
// Is this useful? Required? Bad practice?
.subscribeOn(Schedulers.io())
// Do actions on main thread
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Response>() {
#Override public void call(Response response) {
handleResponse(response);
});
In the current version of Retrofit (1.9.0), Retrofit use his own executor to perform the http call and don't use the executor backed by the schedulers given by the subscribeOn method.
In your case, the scheduler will be used only to execute the code that will add your http call to the executor used by retrofit. (So it's a bit useless...)
BUT, regarding the actual code from Retrofit on Github, retrofit stop to use his executor, so it may be possible to used a RxJava scheduler instead.