I have to improve an old service which is making multiple consecutive calls to the same service and merging all the results in a single list.
Imagine we have a list of IDS = [ 1 ,2 3, .. 6] So I have to call to the same API with each ID (with retrofit and Observables).
For doing this I'd like to use Rx Android but I have doubts about how to merge the results.
Integer[] ids = {1, 2, 3};
Observable.from(ids)
.map(id -> mApi.getData(id))
Can we do parallel calls and merge the result following the same order?
For improving the time response the idea is to do this in Parallel but I don't know how to be sure the result will be combined following the same order we do the server calls.
I thought to use concat (http://reactivex.io/documentation/operators/concat.html) but it waits the previous observable to wait.
See concatMapEager and its variants.
Observable.fromArray(1, 2, 3)
.concatMapEager(id ->
Observable.fromCallable(() -> mApi.getData(id))
.subscribeOn(Schedulers.io())
)
.subscribe(...);
Related
I have two different endpoints I am using to fetch data for a user. I am using retrofit with the RX Adapter factory. If I make a call to both the endpoints inside a single method sequentially is it considered to be a parallel call executing on two different threads. If not how could I make these API calls parallel using RX? or a way to get the response at the same time while fetching the data in parallel. for example, the first endpoint could take 5 seconds while the second takes 7 seconds but the end response would be available after 7 seconds.
fun fetchData() {
api.getData()
.subscribeOn(Schedulers.io())
.subscribe(
{ profileResponse ->
//ProfileResponse Object
Timber.d("profileResponse: $profileResponse")
//store response for later use
Cache.save("key", profileResponse.toString())
},
{
Timber.e("error")
}
)
api2.getData()
.subscribeOn(Schedulers.io())
.subscribe(
{ profileDetails ->
//profileDetails Object
Timber.d("profileDetails: $profileDetails")
},
{
Timber.e("error")
}
)
}
Firstly, you're using subscribeOn() for each observable so it's already executing in parallel.
Is there a way to get the response at the same time while fetching the data in parallel. for example, the first endpoint could take 5 seconds while the second takes 7 seconds but the end response would be available after 7 seconds.
For this, you can use Observable.zip like the following where the time required is the
maximum of two calls:
val disposable = Observable.zip(
firstNetworkCall().subscribeOn(Schedulers.io()),
secondNetworkCall().subscribeOn(Schedulers.io()),
BiFunction{
firstResonse: ResponseOneType,
secondResponse: ResponseTwoType ->
combineResult(firstResponse, secondResponse) }))
.observeOn(AndroidSchedulers.mainThread())
.subscribe { it -> doSomethingWithIndividualResponse(it) }
This article may help to visualize how it's working underneath.
If I make a call to both the endpoints inside a single method sequentially is it considered to be a parallel call executing on two different threads. If not how could I make these API calls parallel using RX?
They are parallel. You're subscribing to a an observable on an IO scheduler and not blocking waiting for responses.
or a way to get the response at the same time while fetching the data in parallel. for example, the first endpoint could take 5 seconds while the second takes 7 seconds but the end response would be available after 7 seconds.
One way is to use zip() to combine your observables to a single observable that emits when all the sources have emitted.
I have stared learning RxAndroid and have understood the reactive programming approach and power of different operators like flatmap, map, and xmap.
I have the following situation, I have a list of Reports say List<Reports> (obtained at runtime). I have to make 3 API calls for a report in a sequence (API 1 -> API 2 -> API 3) and repeat the same process for all reports in a sequence.
Example:
For Reports A, B, C, and D in List<Reports> make 3 API call for A and then for B and then for C and then for D. If anyone of 3 API calls fails for a report then stop the remaining call and continue for the next Report. Here the size of List<Reports> is known at runtime only.
How can I use RXjava to solve this problem?
Supposing you have the 4 API calls, a combineLatest operation would ideally return type Reports.
Observable<Report> result = Observable.combineLatest(
apiA,
apiB,
apiC,
apiD,
(respA, respB, respC, respD) -> respA + respB + respC + respD
)
.subscribeOn(
....
To be able to achieve the sequential order, you will need to wrap them in a flatMap operation:
Observable o = Observable.just(.....)// your initial data
o
.flatMap(value -> doCombineLatestOperation(value))
.subscribe(resp -> //Deal with data);
Hope that helps.
So I have 3 rest services to call, problem is, I need services 1 to finish before calling service 2 and I need service 2 to finish before calling service 3.
Because each time I need to pass data from the previous service to the next.
right now I'm chaining my tasks but I don't like it because method naming becomes quite massive
Example of service 1 ;
compositeDisposable.add(simpleRetrofitService.getInventaireDisponibleResultatDtos()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(this::callService2Then3ThenSaveThenGoToNextScreen,
this::logErrorAndDisplayPopup));
callServiceOneThenTwoThenSaveThenGoToNextScreen()
then define an other asynctask which calls callService3ThenSaveThenFoToNextScreen()
Any ideas?
Thanks.
Edit : precision
Call1() will return an object containing a list of integer like [1, 2, 8, 132]
Then I have to call call2() for each integer.
And same thing for call3(), call2() gives me some values and I have to call Call3() for each values.
For simplicity, I'll assume your calls are simpleRetrofitService.call1(). simpleRetrofitService.call2() and simpleRetrofitService.call3(). I will also assume they are returning http://reactivex.io/RxJava/javadoc/io/reactivex/Single.html
Then you can do some basic stuff like this (using http://reactivex.io/documentation/operators/flatmap.html):
simpleRetrofitService.call1()
.flatMap(r1 -> simpleRetrofitService.call2(r1))
.flatMap(r2 -> simpleRetrofitService.call3(r2))
.subscribeOn(...)
.observeOn(...)
.subscribe(...)
You don't really need AsyncTask at all.
Update as a response to question update:
Assuming your call returns list, you can do something like this (using http://reactivex.io/RxJava/javadoc/io/reactivex/Single.html#flattenAsObservable(io.reactivex.functions.Function)):
simpleRetorfitService.call1()
.flattenAsObservable(...)
.flatMap(listItem -> simpleRetrofitService.call2(listItem)
...
I have list coming back from a REST endpoint. I need to break that list down into categories (category is an item in each entry of the list). Individual categories will be written to a cache for faster lookup later.
I didn't know if I could .map() the entries and supply multiple filter() or some type of case statement to put the category entries in the right bucket.
Does something like this sound reasonable to implement with rxJava?
UPDATE:
Non-working version
private Map<String, List<VideoMetadataInfoEntity>> buildCategories( Observable<List<VideoMetadataInfoEntity>> videoList ) {
Map<String, List<VideoMetadataInfoEntity>> categoryMap = new HashMap<>();
videoList
.flatMap( Observable::from )
.subscribe( videoMetadataInfoEntity -> mapCategory(videoMetadataInfoEntity, categoryMap ) );
Observable.just( categoryMap )
.doOnNext( saveCategoriesToCacheAction );
return categoryMap;
}
These fire in sequence, however, and this is my understanding, the second observable is not sending anything the saveCategoriesToCacheAction since it hasn't subscribed to the result of the first observable.
I am starting to think I should modify my cache strategy. The list will always have all the details. The service doesn't provide me a subset that I can use for listing and then another call to get the full details. It is either full list or full details for one item. It might be a better approach to just cache each one individually and into their own category caches right now. I was trying to do the map so that this network call could return the requested category, but subsequent calls would come from the cache, until such time as the cache has expired and a new network call refreshes it.
My solution is:
Observable.range(1, 20)
.groupBy(number -> number % 2)
.flatMap(groupedObservable -> groupedObservable.toList())
.toMap(list -> list.get(0) % 2);
As a result I have [{0=[2, 4, 6, 8, 10, 12, 14, 16, 18, 20], 1=[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]}]
Explanation:
range(1, 20) - creates an observable which emits first twenty numbers
groupBy(number -> number % 2) - creates an observable that emits group observables where each group observable holds items grouped with the grouping function(here it is x % 2)
flatMap(groupedObservable -> groupedObservable.toList()) - turns each group into an observable that emits all its items as a list
toMap(list -> list.get(0) % 2) - creates the map
RxJava is more for asynchronous message processing, but as it also espouses functional programming principles it could be used as a poor man's stream api. If you are using Java 8 consider using streams to do this job, but as you are asking this question I assume you are using Java 7.
To do what you want you could try (forgive the lambda, substitute it with an anonymous inner class if you are not using Retrolambda):
Observable.from(list).subscribe(item -> groupItemInCategoryBucket(item));
where groupItemInCategoryBucket is your method that contains the switch statement or whatever other way you have of caching the items.
Please note that this is the equivalent of a for loop, and although it is idiomatic to use this style in many other nice languages, a lot of Java developers might be a bit puzzled when they see this code.
Generally grouping of items can be achieved using a groupBy operator (for more information about it visit this page).
Map<Integer, List<Integer>> groupedValues = new HashMap<>(4);
Observable.range(1, 20)
.groupBy(i -> i % 2, i -> i)
.subscribe(go -> {
List<Integer> groupValues = new ArrayList<>();
groupedValues.put(go.getKey(), groupValues);
go.subscribe(t -> add(t, groupValues));
});
How it works:
Firstly, observable emits items 1 through 20 (this happens in range method)
Which then are emitted to separate observables based on their
parity(groupBy method, after this method you operate on GroupedObservable)
You then subscribe to the grouped observable, receiving (in subscribers onNext) separate observables that will contain grouped items and the key they were grouped by.
Remember to either subscribe to the grouped observables or issue take(0) on them if their content does not interest you to prevent memory leaks.
I am not sure whether it is the most efficient way or not and would welcome some input about this solution.
I am playing around with RxAndroid. I have a List of Observables all of which are api requests (using Retrofit). I want to fire one of them every x seconds or milliseconds but then zip the responses together. I seems that once I subscribe to Observable.zip(requests, someFunction) all of them are fired off at once. Any tips?
Thanks!
EDIT: looks like adding delaySubscription to each request maybe the answer
You are looking for either delay() or delaySubscription().
delay() will delay the result of the Observable being published to the subscriber.
delaySubscription() will delay subscription to the Observable.
Observable.zip(someObservable.delaySubscription(100, TimeUnit.MILLISECONDS),
someOtherObservable.delaySubscription(200, TimeUnit.MILLISECONDS),
someThirdObservable.delaySubscription(300, TimeUnit.MILLISECONDS),
new Func3<Object, Object, Object, Void>() {
...
}).subscribe();
Also, it's posible to achieve a periodical sending effect by using the interval() operator.
Let's see a simple example. Imagine you have an array, numbers, whose values have to emitted each x time. You could create an Observable that emits them:
Observable<Integer> values = Observable.from(numbers);
And now, another Observable that emits each (for instance) 30 milliseconds:
Observable<Long> interval = Observable.interval(30, TimeUnit.MILLISECONDS);
So, through the zip() operator you could combine both to achieve the periodical emission of the values in your number array:
Observable.zip(values, interval, (arrayElement, aLong) -> arrayElement)
.subscribe(arrayElement -> doSomething(arrayElement));
I used it to get an animation effect for a progress indicator. I wrote a complete example project you can check in github.