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.
Related
I am working on an application, where I had used Rx-android and Retrofit to do the network request, but now there is a requirement in the project where I have to do the nested network calls.I tried to google it out but didn't found any good article.If any one has worked on such topic then please let me know your findings.
Assuming you're using retrofit with the rxjava adapter:
fun firstRequest(): Single<Response<String>>
fun secondRequest(idFromFirstRequest: String): Single<Response<ResponseBody>>
Use flatmap operator to chain the network calls:
firstRequest()
// do more operators on the request, like transforming the object, or showing it first on the ui
.flatMap { stringId -> secondRequest(stringId) }
// you can flatMap here again to chain another network requests
// .flatMap { thirdRequest() }
// .flatMap { fourthRequest() }
// and so on...
There are various articles related to API chaining, and the easiest way to achieve is using Rx-Java approaches
1) Using RxJava Zip operator (for parallel requests)
2) Using RxJava flatMap() operator(To request serially one after another)
Refer these two links for more detail examples
Synchronizing Network Calls With RxJava
Multiple api request using retrofit and rx java
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()
AFAAIK, Retrofit uses RxJava internally.Then what is the advantage of integrating Retrofit with RxJava externally like here, if I don't want to filter,sort or modify the data received from api?Does it reduces the time for fetching response from api?In what way does it helps in improving performance of our api calls?
Retrofit started as project before RxJava and you used to retrieve the API via callbacks. Then came RXJava and a more strict integration between the two was possible. So that you change Call<T> with an Observable/Flowable interface, and instead to use a call back into the code you retrieve the result directly exploiting the power of the reactive paradigm.
Please consider you have to specify you are using RXJava when you build Retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl);
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//here
.addConverterFactory(GsonConverterFactory.create())
.build();
Saying that RXJava implements internally Retrofit is kind of tricky, Retrofit remain indipendent, just RXJava offers some binding code so that you can adapt Retrofit2 to be an Observable.
This code taken from here, explain how to bind the two
public interface UserService {
#POST("/me")
Observable<User> me();
}
// this code is part of your activity/fragment
Observable<User> observable = userService.me();
observable
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<User>() {
#Override
public void onCompleted() {
// handle completed
}
#Override
public void onError(Throwable e) {
// handle error
}
#Override
public void onNext(User user) {
// handle response
}
});
Then you ask to many questions, in stackoverflow you get one reply per post.
Just please consider that the advantage of using RXJava and Retrofit integrated are a lot, for instance you have a come much more clean, testable and you do not have to consider concurrency issues. Regarding the performance I get is the same for a normal case.
EDIT:
To understand better when to use RXJAVA+Retrofit and when just Retrofit you can see this post
Outside from that content please consider that is really useful to see all the succesion in a functional way inside a single class, plus you have OnComplete, you can operate any sort of transformation.
Furthermore is much easier to combine multiple calls as here, the advantages are really clear in real life situations,
and also to do testing and maintain the code clean, that just taken alone these two, are two great advantages.
You also may want to explore the new Google Architecture functionalities components with Retrofit, where you can use both RXJava or LiveData
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.
I am writing integration tests that perform actions in the UI which start network calls using Retrofit.
I know I need to implement a CountingIdlingResource, but I want to do it the correct way (and not reinvent the wheel if it has already been done).
Has anyone implemented an IdlingResource in their app's Espresso test suite to wait while network requests execute?
More info here.
The most straightforward solution for this: is to basically swap out Retrofit's Thread-pool executor with an AsyncTask one (as recommended by the very helpful Nick from that linked Google group discussion). I do this like so:
new RestAdapter.Builder()
.setEndpoint(LOCLSET_SERVER_URL)
.setExecutors(AsyncTask.THREAD_POOL_EXECUTOR,
new MainThreadExecutor())
.build();
I'm not sure if this is the most appropriate solution, but it's the quickest most sane one that I could get working. Bare in mind the caveat, that this works only for ICS+.
If you're using RxJava Observables with Retrofit 2.0 then you can use .subscribeOn(Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR)) instead of .subscribeOn(Schedulers.io()) and everything works fine!
OR alternatively you can override RxJavaSchedulersHook, allowing you to just make the change in one location. For example:
public MySuperCoolClient() {
if (BuildConfig.DEBUG) {
configureIoSchedulerToUseAsyncTaskThreadPool();
}
this.restApi = new Retrofit.Builder()
.baseUrl(Parameters.endpoint)
.addConverterFactory(GsonConverterFactory.create(gsonBuilder()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build()
.create(RestApi.class);
}
private void configureIoSchedulerToUseAsyncTaskThreadPool() {
RxJavaPlugins.getInstance().registerSchedulersHook(new RxJavaSchedulersHook() {
#Override
public Scheduler getIOScheduler() {
return Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
}
note answer below is based on Retrofit 1.6.1 - will update for newest version. Retrofit 1.9.0 does not allow you to set the HttpExecutor via the RestAdapter.Builder any longer
The accepted answer is a step in the right direction but it makes me feel uncomfortable. In practise you would either need to set the AsyncTask.THREAD_POOL_EXECUTOR for live & tests builds OR test builds only.
Setting for both would mean all your network IO pooling will depend on the aysnc queue implementation, which became serial by default for apps with target versions ICS+
Setting for tests only would mean that your test build is different from your live build, which imho is not a great place to start testing from. Also you may encounter test problems on older devices due to async pool changes.
It is rightly mentioned above that Espresso hooks into AsyncTask.THREAD_POOL_EXECUTOR already. Lets poke around...
How does it obtain this?
ThreadPoolExecutorExtractor
Who/what uses this?
BaseLayerModule has provideCompatAsyncTaskMonitor(ThreadPoolExecutorExtractor extractor) which returns an AsyncTaskPoolMonitor
How does that work? Have a look!
AsyncTaskPoolMonitor
Where is it used?
UiControllerImpl has method loopMainThreadUntilIdle() which manually calls asyncTaskMonitor.isIdleNow() before checking any user registered idlingResources with idlingResourceRegistry.allResourcesAreIdle()
Im guessing with Retrofit we can use the RestAdapter.Builder.setExecutors(...) method and pass in our own instance (or version) of the AsyncTaskPoolMonitor using the same http Executor that Retrofit is init on Android with
#Override Executor defaultHttpExecutor() {
return Executors.newCachedThreadPool(new ThreadFactory() {
#Override public Thread newThread(final Runnable r) {
return new Thread(new Runnable() {
#Override public void run() {
Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
r.run();
}
}, RestAdapter.IDLE_THREAD_NAME);
}
});
}
(from here)
And wrap this in the IdlingResource interface to use in our tests!!
The only question in that as Retrofit makes the callback using a separate Executor on the mainThread that relies on the main Looper, this may result in problems but Im assuming for the moment that Espresso is tied into this as well. Need to look into this one.
Retrofit 2 uses okhttp3, which, in turn uses a dispatcher. Jake Wharton created this library that monitors the dispatcher for idleness. You would create the IdlingResource like this:
IdlingResource resource = OkHttp3IdlingResource.create("OkHttp", okHttpClient);
Be aware that this might not suffice to be used for successful Espresso tests (I've tried) because the IdlingResource might say it's idle just before or after the http call, and your Espresso test would execute and fail instead of waiting.
My recommendation for these cases is to use a thread pool to launch any background tasks and make an IdlingResource wrapping this thread pool. See this article for more info: https://medium.com/#yair.kukielka/idlingresource-dagger-and-junit-rules-198e3ae791ff
If you're using Asynctasks, you don't need to do anything because Espresso already knows how to wait for them: it uses AsyncTaskPoolMonitor which is a wrapper around the Asynctask thread pool.
If you're using you're own thread pool (that was my case), you could use this class that will wrap your executor so that Espresso can know when it's idle.
This great post explains how it works. I tried in my project and it's great! Using dagger, I get a hold of my thread pool and wrapped it in an IdlingResource in a junit #rule.