RXAndroid/RXJava2: Chain three retrofit requests and repeat the same process again - android

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.

Related

RxJava2 and Android complex observable chaining

I have been working with Rx Java 2 for awhile but recently came across a situation that has stumped me. I have a semi-complex chain of operations and wish to pass a "state object" down the chain.
There are 4 operations during which I wish to repeat operations 2 and 3 (serialy, not together) until certain conditions are true. I know i can solve this by chaining each operation using andThen(), but this limits my ability to pass a state object down the chain without reaching outside of the chain.
The reason I need to have a state object is because I need to save an initial value during the first operation and compare it to a value recieved during operation 4 to determine if the overall procedure was successful.
Any clues as to what RxJava2 operators can help me achieve the proper repeat conditions for operation 2 and 3? I would prefer to not nest observables if possible.
You can keep your state as some AtomicReference<State> and use repeatUntil operator.
AtomicReference<State> state = new AtomicReference<>();
Completable operation = Completable.create() // do something and modify state
.repeatUntil(() -> state.get() == SATISFYING_CONDITION);
You can easily chain these Completables with andThen

Problems understanding RX philosophy (RxJava, RxJS, Rx...) on android

Here a case with using reactive programming (with RxJava for instance)
There is a User object with some properties (name, surname) - the observable
An activity contains 2 fragments both showing the current user name - the subscribers
The user changes (name changes)
Is the assumption right that the displayed name should change automatically if the observed source of data changes (if both fragments are subscribed to the same user object)?
From what I have seen now all examples about rxjava and android focus on async calls and handling streams of returned data triggered/called by subscription. What should / will happen if the source being observed changes? Are subscribers supposed to be triggered or not?
Taken from here:
https://en.wikipedia.org/wiki/Reactive_programming
For example, in an imperative programming setting, a:=b+c would mean that a is being assigned the result of b+c in the instant the expression is evaluated, and later, the values of b and c can be changed with no effect on the value of a. However, in reactive programming, the value of a would be automatically updated whenever the values of b and c change, without the program executing the sentence a:=b+c again.
Are there any examples of how to setup the behavior with rxJava as described in the wikipedia article?
If you create observable that shares updates on the User object(hint: subject/operator) and both fragments get the same observable of the user and subscribe to it then they will get the new version of the User.
As to the Wikipedia example, given you express b and c as observables this can be easily done using combineLatest operator:
Subject<Integer> sb;
Subject<Integer> sc;
Observable.combineLatest(sb, sc, (b, c) -> b + c)
.subscribe(outcome ->
System.out.println("Always up to date value here: " + outcome)
);

Rx Android combine multiple calls to the same service in parallel

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(...);

rxJava transform List to Map

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.

Observable zip order

I got 3 observable objects
Observable<CharSequence> o1 = RxTextView.textChanges(quitBuddyName);
Observable<CharSequence> o2 = RxTextView.textChanges(quitBuddyPhone);
Observable<CircleInviteListAdapter> o3 = RxAdapter.dataChanges(listAdapter);
Observable.zip(o1, o2, o3, (a, b, c) -> {
return a.length() > 0 && b.length() > 0 && c != null;
}).subscribe(finishBtn::setEnabled);
Question: Why finish button is not enabled after I set adapter first then enter text?
You're after combineLatest, not zip.
Zip needs results from all sources to produce a new one.
CombineLatest triggers after a change in either of the sources, but only after all 3 sources have produced at least one value.
Can't be sure from the code but here are 2 guesses.
1 - An error occurs. You're not doing anything onError so if an error occurs you're never going to know about it.
2 - Your Observable isn't emitting anything. Again, as you're not performing an Action onComplete you won't know if your Observable completes without emitting.
As noted in the documentation
It will only emit as many items as the number of items emitted by the
source Observable that emits the fewest items
So if one of your source Observables completes without emitting, then your zip doesn't do anything and your Subscribe just calls onComplete
In general, I would always recommend implementing at least onNext & onError, and usually I implement onComplete, even if it's to just log something at debug level.

Categories

Resources