I need to call 3 different api ...each api takes input of another api output in sequence..
eg : API1 -> output -> will be input for API2
API2 -> output -> will be input for API3
In my case , Spinner contain API1 ....On spinner selection i need to call API2 and so on
Currently i am writing a separate code for each API and call them using Observer...but i want to call APIs in sequence using RxJava,RxKotlin and Retrofit flatmap concepts.So is there any way using that, i can call this three APIs in sequence, without writing each of them seperatly
Added snippets from links shared by Surinder
public Single<List<Restaurant>> getRestaurants(int userId) {
return ddApi.getUserInfo(userId).flapMap(user -> {
return ddApi.getAvailableRestaurants(user.defaultAddress.lat,
user.defaultAddress.lng);
});
}
public class RestaurantFragment {
private CompositeDisposables disposables = new CompositeDisposables();
private RestaurantDataSource restaurantDataSource;
#Override
public void onResume() {
// subscribe to the Single returned by RestaurantApi
restaurantDataSource
.getRestaurants(userId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Restaurant>() {
#Override
public void onSubscribe(Disposable d) {
disposables.add(d);
}
#Override
public void onSuccess(List<Restaurant> restaurants) {
// update the adapter with restaurants
}
#Override
public void onError(Throwable e) {
// display an error message
}
});
}
#Override
public void onPause() {
disposables.clear();
}
}
you can follow below link for chaining of API calling using flatMap
if using Kotlin then follow
Chaining Multiple Retrofit Call Using RxJava/ Kotlin
Related
TL;DR: I want to execute multiple Calls (Retrofit) like you can .zip() multiple Observables (RxJava2).
I have a retrofit2 function:
#GET("/data/price")
Call<JsonObject> getBookTitle(#Query("id") String id, #Query("lang") String lang);
I can execute it (async) in code with enquene():
ApiProvider.getBooksAPI().getBookTitle(bookId, "en").enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) { }
#Override
public void onFailure(Call<JsonObject> call, Throwable t) { }
});
Now I want to execute multiple Calls at once (get multiple book titles) and be notified when all requests are done. Here is when I am missing knowledge.
I know I could start using Observable (RXJava2) instead of Call (Retrofit2):
#GET("/data/price")
Observable<JsonObject> getBookTitle(#Query("id") String id, #Query("lang") String lang);
and then merge calls like in below example. But this code seems much more complex and long (especially if I only need 1 book title). Isn't there any way I could merge Calls without using Observable?
List<Observable<JsonObject>> mergedCalls = new ArrayList<>();
mergedCalls.add(ApiProvider.getBooksAPI().getBookTitle(bookId1, "en"));
mergedCalls.add(ApiProvider.getBooksAPI().getBookTitle(bookId2, "en"));
mergedCalls.add(ApiProvider.getBooksAPI().getBookTitle(bookId3, "en"));
Observable<List<JsonObject>> observable = Observable.zip(calls, responses -> {
// merge responses, return List
...
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io());
observer = new DisposableObserver<List<JsonObject>> () {
#Override
public void onNext(List<JsonObject> result) { // got all API results }
#Override
public void onError(Throwable e) { }
#Override
public void onComplete() { }
};
observable.subscribe(observer);
Using RxJava is the easy way of merging Retrofit Calls. Merging Calls manually by enqueuing all Calls and doing something when all of them invoke onResponse, will probably be more complex than simply using Observable.zip(...).
The other choice that you have is using Kotlin coroutines (now Retrofit has out of the box support for them). But that depends on the Kotlin presence in your code and your willingness of using coroutines.
EDIT:
(Answering your question from the comment)
If you really think about Calls and RxJava Observables you don't really have to do anything more when using RxJava. When using raw Calls you still have to:
Make sure you're on the right thread if you want to touch Views (observeOn(AndroidSchedulers.mainThread()))
Make sure you're touching network on the right thread (subscribeOn(Schedulers.io()))
Make sure you're not using the response when your Activity/Fragment/Something else is no longer present (disposing of the Disposable in RxJava handles that)
You can significantly simplify your example:
Don't create Observable & Observer. Simply use the subscribe method which returns Disposable. And then maintain just this one Disposable.
You probably don't need onComplete so you can use the simpler version of .subscribe(...)
You can remove the need for .subscribeOn(Schedulers.io()) by properly creating your RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()) when building the Retrofit instance.
BooksApi booksApi = ApiProvider.getBooksAPI();
List<Observable<JsonObject>> mergedCalls = new ArrayList<>();
mergedCalls.add(booksApi.getBookTitle(bookId1, "en"));
mergedCalls.add(booksApi.getBookTitle(bookId2, "en"));
mergedCalls.add(booksApi.getBookTitle(bookId3, "en"));
final Disposable disposable = Observable
.zip(mergedCalls, responses -> {
// merge responses, return List
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(list -> {
// got all API results
}, throwable -> {
});
Doing that for one call would be as simple as:
final Disposable disposable = booksApi
.getBookTitle(bookId1, "en")
.observeOn(AndroidSchedulers.mainThread())
.subscribe(title -> {
// got the result
}, throwable -> {
});
I've been looking around StackOverflow and other Android-related sites to try and get a grasp on this, but I'm still struggling.
I'm using Retrofit to make calls to an API as follows:
public interface TheMovieDbApi {
#GET("genre/{type}/list")
Observable<GenresReply<Genre>> getGenreList(#Path("type") String type);
}
The above example returns an Object (GenresReply) which contains a List of Genres.
I need to make this call twice - once for movies, once for TV - and combine the results. Having looked at other examples here, I've come up with the following:
private void loadGenres() {
List<Observable<?>> requests = new ArrayList<>();
requests.add(api.getGenreList("movie"));
requests.add(api.getGenreList("tv"));
//Now what?
}
I'm lost on the next step. I've seen examples using Observable.concat(), .flatMap() and .zip() and then subscribing to the output, but I'm not familiar enough with RxJava to know what to do next.
TL;DR How do I make two API calls and extract the List of Genres from each response/the combined List of Genres?
Solution
Thanks to the comments from John and masp, here's what I've come up with:
private void loadGenres() {
Observable.zip(api.getGenreList(MOVIE_GENRES), api.getGenreList(SHOW_GENRES),
new BiFunction<GenresReply<Genre>, GenresReply<Genre>, List<Genre>>() {
#Override
public List<Genre> apply(GenresReply<Genre> movieReply, GenresReply<Genre> showReply)
throws Exception {
List<Genre> genreList = new ArrayList<>();
genreList.addAll(movieReply.getGenres());
genreList.addAll(showReply.getGenres());
return genreList;
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Genre>>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
}
#Override
public void onNext(#NonNull List<Genre> genres) {
DatabaseUtils.insertGenres(genres, ListActivity.this);
}
#Override
public void onError(#NonNull Throwable e) {
mSharedPreferences.edit().putBoolean(FIRST_RUN, true).apply();
}
#Override
public void onComplete() {
}
});
}
You should be able to do something like:
Observable.zip(api.getGenreList("movie", api.getGenreList("tv", (movieInfo, tvInfo) -> Pair.create(movieInfo, tvInfo)).subscribe(movieTvPair -> {
})
I just start learning rxJava for Android and want to implement the common use case:
request data from cache and show to the user
request data from web
server update data in storage and automatically show it to the user
Traditionally on of the best scenarios was use CursorLoader to get data from cache, run web request in the separate thread and save data to the disk via content provider, content provider automatically notify the listener and CursorLoader autoupdate UI.
In rxJava I can do it by running two different Observers as you can see in code below, but I don't find the way how to combine this two calls into the one to reach my aim. Googling shows this thread but it looks like it just get data from the cache or data from the web server, but don't do both RxJava and Cached Data
Code snippet:
#Override
public Observable<SavingsGoals> getCachedSavingsGoal() {
return observableGoal.getSavingsGoals()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
#Override
public Observable<SavingsGoals> getRecentSavingsGoal() {
return api.getSavingsGoals()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
model.getCachedSavingsGoal().subscribe(new Observer<SavingsGoals>() {
#Override
public void onCompleted() {
// no op
}
#Override
public void onError(Throwable e) {
Log.e(App.TAG, "Failed to consume cached data");
view.showError();
}
#Override
public void onNext(SavingsGoals savingsGoals) {
Log.d(App.TAG, "Show the next item");
if (savingsGoals != null && !savingsGoals.getSavingsGoals().isEmpty()) {
view.showData(savingsGoals.getSavingsGoals());
} else {
view.showError();
}
}
});
model.getRecentSavingsGoal().subscribe(new Observer<SavingsGoals>() {
#Override
public void onCompleted() {
// no op
}
#Override
public void onError(Throwable e) {
Log.e(App.TAG, "Failed to consume data from the web", e);
view.showError();
}
#Override
public void onNext(SavingsGoals savingsGoals) {
if (savingsGoals != null && !savingsGoals.getSavingsGoals().isEmpty()) {
view.showData(savingsGoals.getSavingsGoals());
} else {
view.showError();
}
}
});
Also, the one of issues with current approach is cache and web data are not garranted to be run sequently. It is possible when outdated data will come as latest and override recent from web.
To solve this issue I implemented Observer merge with filtration by timestamp: it get data from cache, pass it to the next observer and if cache is outdated fire new call to the web - case for thread competition solved by the filtration with timestamps. However, the issue with this approach I can not return cache data from this Observable - I need to wait when both requests finish their work.
Code snippet.
#Override
public Observable<Timestamped<SavingsGoals>> getSavingGoals() {
return observableGoal
.getTimestampedSavingsGoals()
.subscribeOn(Schedulers.io())
.flatMap(new Func1<Timestamped<SavingsGoals>, Observable<Timestamped<SavingsGoals>>>() {
#Override
public Observable<Timestamped<SavingsGoals>> call(Timestamped<SavingsGoals> cachedData) {
Log.d(App.FLOW, "getTimestampedSavingsGoals");
return getGoalsFromBothSources()
.filter(filterResponse(cachedData));
}
})
.subscribeOn(AndroidSchedulers.mainThread());
}
private Func1<Timestamped<SavingsGoals>, Boolean> filterResponse(Timestamped<SavingsGoals> cachedData) {
return new Func1<Timestamped<SavingsGoals>, Boolean>() {
#Override
public Boolean call(Timestamped<SavingsGoals> savingsGoals) {
return savingsGoals != null
&& cachedData != null
&& cachedData.getTimestampMillis() < savingsGoals.getTimestampMillis()
&& savingsGoals.getValue().getSavingsGoals().size() != 0;
}
};
}
private Observable<Timestamped<SavingsGoals>> getGoalsFromBothSources() {
Log.d(App.FLOW, "getGoalsFromBothSources:explicit");
return Observable.merge(
observableGoal.getTimestampedSavingsGoals().subscribeOn(Schedulers.io()),
api.getSavingsGoals()
.timestamp()
.flatMap(new Func1<Timestamped<SavingsGoals>, Observable<Timestamped<SavingsGoals>>>() {
#Override
public Observable<Timestamped<SavingsGoals>> call(Timestamped<SavingsGoals> savingsGoals) {
Log.d(App.FLOW, "getGoalsFromBothSources:implicit");
return observableGoal.saveAllWithTimestamp(savingsGoals.getTimestampMillis(), savingsGoals.getValue().getSavingsGoals());
}
}))
.subscribeOn(Schedulers.io());
}
Do you know the approach to do this in one Observer?
Potential solution:
#Override
public Observable<SavingsGoals> getSavingGoals() {
return api.getSavingsGoals()
.publish(network ->
Observable.mergeDelayError(
observableGoal.getSavingsGoals().takeUntil(network),
network.flatMap(new Func1<SavingsGoals, Observable<SavingsGoals>>() {
#Override
public Observable<SavingsGoals> call(SavingsGoals savingsGoals) {
return observableGoal.saveAll(savingsGoals.getSavingsGoals());
}
})
)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
Sorry, hot replacement in IDE hide the issue which this approach has: first one in case if network unavailable and cache thread completes first, the error will terminate whole merge (solved by mergeDelayError), second one is in case when cache is empty and returns first data from web request will not be return on subscriber. As you can see my method returns Observable after save and traditional merge as I shown in my code properly handle this case but takeUntil by some reason can not. Question is still open.
For first question : You can save the result from Network Result by using doOnNext Method, It would looks something like this
public Observable<NetworkResponse> getDataFromNetwork(
final Request request) {
return networkCall.doOnNext(networkResponse -> saveToStorage(networkResponse);
}
Now to combine the two results from both Storage and Online, the best way is to combine with publish and merge. I recommend watching this talk.
The code would look something like this
public Observable<Response> getData(final Request request) {
return dataService.getDataFromNetwork(request)
.publish(networkResponse -> Observable.merge(networkResponse, dataService.getDataFromStorage(request).takeUntil(networkResponse)));
}
Why use publish and merge you my ask? publish method makes the response accessible in the callback. takeUntil means that you will take the data from storage but you will stop it IF for some reason, network call is finished before accessing storage data is finished. This way, you can be sure that new data from network is always shown even if it's finished before getting old data from storage.
The last but not least, in your subscriber OnNext just add the items to the list. (list.clear and list.addAll) Or similar functions or in you case view.showData()
EDIT: For The call getting disrupted when there's an error from network, add onErrorResumeNext at the end.
public Observable<Response> getData(final Request request) {
return dataService.getDataFromNetwork(request)
.publish(networkResponse -> Observable.merge(networkResponse, dataService.getDataFromStorage(request).takeUntil(networkResponse)))
.onErrorResumeNext(dataService.getDataFromStorage(request);
}
I'd recommend to "listen" only to local data, and refresh it when API response came.
Let say for getting local data you have something like:
#Nonnull
public Observable<SomeData> getSomeDataObservable() {
return Observable
.defer(new Func0<Observable<SomeData>>() {
#Override
public Observable<SomeData> call() {
return Observable.just(getSomeData());
}
});
}
So you need to add PublishSubject that will emit every time, when local data was updated (refreshSubject):
#Nonnull
public Observable<SomeData> getSomeDataObservableRefreshable() {
return refreshSubject.startWith((Object)null).switchMap(new Func1() {
public Observable<T> call(Object o) {
return getSomeDataObservable();
}
}
}
Now you need to subscribe only to getSomeDataObservableRefreshable(), and each time when data came from API, you update it and make refreshSubject .onNext(new Object())
Also i'd recommend to take a look to rx-java-extensions lib, it has alot of "cool tools" for RxAndroid. For example solution for your problem would be:
#Nonnull
public Observable<SomeData> getSomeDataObservable() {
return Observable
.defer(new Func0<Observable<SomeData>>() {
#Override
public Observable<SomeData> call() {
return Observable.just(getSomeData());
}
})
.compose(MoreOperators.<SomeData>refresh(refreshSubject));
}
I'm making a simple weather app to learn RxAndroid and I'm faced with the following issue.
I first load cities I'm interested in and then ask for the weather of each one of them.
getCitiesUseCase returns an Observable<List<City>> that I load from the data base. I send that list of cities to my view to display them and then ask for the weather individually (flatmap) inside the subscriber.
Subscription subscription = getCitiesUseCase.execute().flatMap(new Func1<List<City>, Observable<City>>() {
#Override
public Observable<City> call(List<City> cities) {
citiesView.addCities(cities);
return Observable.from(cities);
}
}).subscribe(new Subscriber<City>() {
#Override
public void onCompleted() {
subscriptions.remove(this);
this.unsubscribe();
}
#Override
public void onError(Throwable e) {
Log.e(this.getClass().getSimpleName(), e.toString());
}
#Override
public void onNext(City city) {
getCityWeatherUseCase.setLatLon(city.getLat().toString(), city.getLon().toString(), city.getId());
getCityWeather(city);
}
});
subscriptions.add(subscription);
Now the getCityWeather() method looks like this:
private void getCityWeather(final City city) {
subscriptions.add(getCityWeatherUseCase.execute().subscribe(new Subscriber<CityWeather>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Log.e("error", e.toString());
}
#Override
public void onNext(CityWeather cityWeather) {
city.setCityWeather(cityWeather);
citiesView.updateCity(city);
}
}));
}
Everything works fine and as expected, but the fact that I'm subscribing to an observer inside a subcriber doesnt feel right. I know rxJava lets you play around with subscribers to prevent this kind of things but I really dont know how to improve my code further. Keep in mind that I need a city in order to ask for its weather.
Merry chrismas!
One approach could be the following. (I'm using retrolambda - so wherever you see ->, just replace with a new anonymous inner class).
Note that I'm using flatMap to spin up the weather data requests, rather than Observable.concat like your question suggests. The reason for this is that your scheduler (e.g. io()) will handle these in parallel and send the results through when they are available. However, with Observable.concat, these requests would be serialized so they'd be forced to happen one at a time - nullifying the benefits of a thread pool like io().
private class City {
public String name;
public City(String name) {
this.name = name;
}
public void setWeather(Weather weather) { /*...*/ }
}
private class Weather {
public String status;
public Weather(String status) {
this.status = status;
}
}
private Observable<Weather> getWeather(City city) {
// call your weather API here..
return Observable.just(new Weather("Sunny"));
}
#Test
public void test() {
Observable<List<City>> citiesObs = Observable.create(new Observable.OnSubscribe<List<City>>() {
#Override
public void call(Subscriber<? super List<City>> subscriber) {
// do work
final List<City> cities = new ArrayList<>();
cities.add(new City("Paris"));
cities.add(new City("Tokyo"));
cities.add(new City("Oslo"));
// send results
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(cities);
subscriber.onCompleted();
}
}
});
Observable<City> obs = citiesObs
// inject a side effect
.doOnNext(list -> {
// pass `list` to your view here
})
// turn Observable<Iterable<T>> into Observable<T>
.flatMapIterable(list -> list)
// Map a city to an observable that fetches Weather data
// Your scheduler can take care of these at once.
.flatMap(city -> {
return getWeather(city)
// another side effect
.doOnNext(weather -> {
city.setWeather(weather);
})
// map baack to city, just for the heck of it
.map($ -> city);
});
TestSubscriber sub = TestSubscriber.create();
obs.subscribe(sub);
sub.awaitTerminalEvent();
sub.assertValueCount(3);
}
Also note that in order to take advantage of io(), you'd need to add a call to subscribeOn(Schedulers.io()) to tell the observable to begin doing work on the io thread pool. When you want to pass control to another thread, for example your view, you could insert a observeOn(AndroidSchedulers.mainThread()) before your side-effect (or mapping). If you want to bounce control back to the background thread(s) for your weather calls, you could then add another call to observeOn(Schedulers.io()) right before you flatMap to getWeather(City).
Is it acceptable to create Rx Observables in custom Application subclass. Reason for doing is, I can create BehaviorSubject inside the Application and will ask for changes every 10 minutes from Server, every Activity or Fragment which subscribes to this Observable will get only last state of changes.
Question is whether this architecture could be considered safe in terms of application lifecycle handling and easy to use?
class CustomApplication extends Application {
...
BehaviorSubject<Friends> mFriends = new BehaviorSubject<Friends>;
public void createObservables() {
Observable.create(new Observable.OnSubscribe<Friends>() {
public void call(Subscriber<?> s) {
while(true) {
mFriends.onNext("randomFriendN");
sleep(10sec);
}
}
})
.subscribeOn(Schedulers.newThread())
.subscribe(new Observer<List<NewsCategory>>() {
public void onNext(Friends f) { //empty }
});
}
public BehaviorSubject<Friends> getFriends() {
return mFriends;
}
}
UPDATE:
Everytime when new activity created and it wants to get data it can get it ApplicationContext's BehaviorSubject then subscribe to it, and Subject will emit last emitted value;
Why I want to do like this? E.g. Lets say you have news items, you fetched news feed and you want to start background task which fetches news item full content, in that case I can start fetching data while you are scrolling news list, and when you click detailed activity, we can show it from already fetched, or just download it.
I think this is perfectly safe as long as createObservables() is only called once during application initialization. A few suggested changes...
I wouldn't expose the BehaviorSubject part of mFriends in the returned value from getFriends(). That way callers of getFriends() will not be tempted to call onNext(). Change it to:
public Observable<Friends> getFriends() {
return mFriends;
}
If you want to be super safe use .asObservable() and callers will not even be able to cast the return value back to a BehaviorSubject.
public Observable<Friends> getFriends() {
return mFriends.asObservable();
}
I would also update your createObservable() method to call the BehaviorSubject onNext() from the subscribe callback. Here is your code slightly modified to use NewsItems.
BehaviorSubject<List<NewsItem>> mNewsItemSubject = BehaviorSubject.create();
void createObservables() {
Observable
.timer(10, 10, TimeUnit.SECONDS, Schedulers.newThread())
.flatMap(new Func1<Long, Observable<List<NewsItem>>>() {
#Override
public Observable<List<NewsItem>> call(Long aLong) {
// Normally you would create a network API that returns Observable<NewsItem>.
// For now just pretend this returned Observable makes an Observable
// network request.
return Observable.just(
Arrays.asList(
new NewsItem("fakeNewsItem"),
new NewsItem("fakeNewsItem1")
)
);
}
})
.subscribe(new Action1<List<NewsItem>>() {
#Override
public void call(List<NewsItem> newsItems) {
mNewsItemSubject.onNext(newsItems);
}
});
}
public Observable<List<NewsItem>> observeNewsItems() {
return mNewsItemSubject;
}
Your Android Activities can then call ((CustomApplication)getApplication()).observeNewsItems() to get the latest news items and any updates while the Activity is visible.
final Observable<List<NewsItem>> newsItemsObservable =
((CustomApplication) getApplication()).observeNewsItems();
newsItemsObservable
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<NewsItem>>() {
#Override
public void onCompleted() {
// All done.
}
#Override
public void onError(Throwable e) {
// Notify user of error (maybe)
}
#Override
public void onNext(List<NewsItem> newsItems) {
// Update the UI with newsItems.
}
});