RxJava - How to get Single from nested method - java

In my Presenter i have a method which gets some list from DataHolder:
disposable.add(dataHolder.getMonthOfAttractions(monthInAdvance)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<Map<String, List<Attraction>>>() {
#Override
public void onSuccess(Map<String, List<Attraction>> stringListMap) {
}
#Override
public void onError(Throwable e) {
}
}));
Then, in my DataHolder I'm checking if my list isn't null. If true, returns my list, if false it downloads this list from server :
public Single<Map<String, List<Attraction>>> getMonthOfAttractions(int monthInAdvance) {
Map<String, List<Attraction>> monthOfAttractions = monthlyAttractionsMap.get(monthInAdvance);
if (monthOfAttractions != null)
return Single.fromCallable(() -> monthOfAttractions);
else
return apiGetMonthOfAttractions(monthInAdvance);
The problem is with apiGetMonthOfAttractions method. I dont know how to correctly implement this method to return value to my Presenter.
I've tried something like:
private Single<Map<String, List<Attraction>>> apiGetMonthOfAttractions(int monthInAdvance) {
cnkRetrofitProvider.getApiInterface().getAttractions(monthInAdvance)
.subscribeWith(new CnkApiObserver<AttractionListResponse>() {
#Override
public void onSucceeded(AttractionListResponse result) {
result.getMonthOfAttractions();
}
#Override
public void onFailed(Error error) {
}
});
}
But in this case i have "missing return statement" and I'm out of ideas how to implement it. I'm begging to learn RxJava, so be understanding.
Please help :)
EDIT:
This is what how my Retrofit getAttractions() method looks like:
public interface CnkApiInterface {
#GET("pl/front-api/{dateFrom}/{dateTo}")
Single<AttractionListResponse> getAttractions(#Path("dateFrom") String dateFrom, #Path("dateTo") String dateTo);}

This is what you are after:
private Single<Map<String, List<Attraction>>> apiGetMonthOfAttractions(int monthInAdvance) {
return cnkRetrofitProvider.getApiInterface()
.getAttractions(monthInAdvance)
.flatMap(attractionListResponse -> Single.just(attractionListResponse.getMonthOfAttractions()));
}

I thing just try to do something like (it only depends what does your cnkRetrofitProvider.getApiInterface().getAttractions(monthInAdvance) returns)
private Single<Map<String, List<Attraction>>> apiGetMonthOfAttractions(int monthInAdvance) {
return cnkRetrofitProvider.getApiInterface().getAttractions(monthInAdvance)
}
should do the trick

You can always just map the result to List<Attraction> so #wojech_maciejewski s answer still holds, you jast need to add a mapping function.
private Single<Map<String, List<Attraction>>> apiGetMonthOfAttractions(int monthInAdvance) {
return cnkRetrofitProvider.getApiInterface().getAttractions(monthInAdvance)
.map(atractions -> /* convert to List<Attraction> here */)
}

Related

How to emit whole list only once even if range() operator iterate itself corresponding to range passed in it

Problem
I am having a list which is coming from the backend after calling some API, I need to convert that list into some other type of list. Afterward, I need to add n number of an empty object into the final list. (n number is coming from some logic which suits my application.)
To solve this problem I have written the following code :
Observable.fromIterable(snipList).map(new Function<UserSnipModel, TaskContent>() {
#Override
public TaskContent apply(UserSnipModel userSnipModel) throws Exception {
String displayName = userSnipModel.getDisplayName();
TaskContent.Fields fields = new TaskContent.Fields(userSnipModel.getId(),
displayName, userSnipModel.getUrlCall(), userSnipModel.getRemarks(), userSnipModel.isEnableCall());
Drawable icon = DrawableUtils.getDrawable(HomeActivity.this, userSnipModel.getIconName(), R.drawable.ic_diamond);
return new TaskContent(fields, icon);
}
}).toList().toObservable().flatMap(new Function<List<TaskContent>, ObservableSource<List<TaskContent>>>() {
#Override
public ObservableSource<List<TaskContent>> apply(List<TaskContent> taskContents) throws Exception {
return Observable.range(0, totalNumberOfMembers - numberOfItems).map(new Function<Integer, List<TaskContent>>() {
#Override
public List<TaskContent> apply(Integer integer) throws Exception {
taskContents.add(new TaskContent(new TaskContent.Fields("",
"", "", "", false), null));
return taskContents;
}
});
}
})
.observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()).safeSubscribe(new Observer<ListTaskContent>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(List<TaskContent> taskContents) {
//This method is called multiple time.
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
}
});
When I run this, onNext() is called multiple time which is not working as expected. To handle this problem I added toList() inside the flatMap() then I got List<List<TastContent>> which is also not expected as I need only List<TaskContent>
What can I do to handle this problem?
Thanks in advance.
I'd suggest this solution:
Observable.fromIterable(list)
.map(item -> item.concat("new")) //Converting the items in the initial list
.toList()
.flatMap(data -> Single.zip(
Single.just(data), //Mapped data set
Observable.range(0, data.size()) //Empty list generator
.map(rangeVal -> rangeVal.toString())
.toList(),
(initialList, emptyList) -> {
initialList.addAll(emptyList); //Appending lists
return initialList;
}
)
)
.subscribe(data -> {}, error -> {});

RxJava sewing two queries

How to sew two Observable in RxJava ?
Observable<List<CalendarEvent>>, for each CalendarEvent, I want to do network operation to read the (lat,lon) and fetch place name, and then combine place name back to CalendarEvent.
public Observable<List<CalendarEvent>> getEvents() {
// get events
// translate each Event LatLng to Place and bind it to Event
// return the events
}
public Observable<List<CalendarEvent>> getEvents() {
List<CalendarEvent> sourceList = ...
return Observable.from(sourceList) //emits each item separately
.concatMap(calendarEvent -> applyPlaceName(calendarEvent)) //fetches places and applies them for each item
//fyi: concatMap executes requests sequentially, if you want do it in parallel - use flatMap instead
.toList(); //collects items to list
}
//somewhere in your Networking class
public Observable<CalendarEvent> applyPlaceName(CalendarEvent calendarEvent) {
return Observable ... //do network call and apply placeName on item
}
//p.s. don't forget to apply appropriate Schedulers
No need for something fancy here, this would roughly do what you want I think:
public class Foobar {
void doSomethingWithEvents() {
getEvents().subscribe(new Action1<List<CalendarEvent>>() {
#Override
public void call(List<CalendarEvent> calendarEvents) {
for (CalendarEvent event : calendarEvents) {
getPlaceForEvent(event).subscribe(new Action1<Place>() {
#Override
public void call(Place place) {
event.setPlace(place);
}
});
}
}
});
}
Observable<Place> getPlaceForEvent(CalendarEvent event) {
return Observable.just(new Place());
}
Observable<List<CalendarEvent>> getEvents() {
return Observable.just(new ArrayList<CalendarEvent>());
}
}

RxJava and Retrofit Chain multiple Calls from diffrent Services and Conditions

i don´t get the right way. The Problem is, that i have two Retrofit API´s and Services and need two wait for response and make a decision and based on this other calls.
Example:
APIx:
#GET("xyz/{id}/exists")
Observable<Exists> checkObjectExists(#Path("id") String id);
#POST("xyz/")
Observable<Object> addObjectA(#Body Object a);
APIy:
#POST("abc/{id}/blabla")
Observable<Object> addObjectB(#Path("id") String id, #Body Object b);
Now the Use case:
I need to do a Request if some Object exists like:
serviceA.exists(id).flatMap(exists -> if(exists) ...
if Exists is true then i need to call
serviceB.addObjectB(b)
Then the first flow is finish.
if Exists is false i need to call
serviceA.addObject(a)
and then when the i get a Success in onNext i need to call
ServiceB.addObject(b)
again. But i really dont get the Chain with RxJava and Retrofit. I can handle this stuff with a lot lines of Code with something like this:
private void _checkExists() {
ServiceA serviceA= ServiceA.create();
serviceA.checkObjectExists(id)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<Exists>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Timber.d("Something get Wrong");
}
#Override
public void onNext(Exists exists) {
if(exists) {
_addObjectB(b);
} else {
addobjectA(a);
}
}
});
}
private void addObjectA(Object a) {
ServiceA serviceA= ServiceA.create();
serviceA.addObjectA(a)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<Object>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Timber.d("Something get Wrong");
}
#Override
public void onNext(Object a) {
addObjectB();
}
});
}
private void addObjectB() {
ServiceB serviceB= ServiceB .create();
serviceB.addObjectB(id, b)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Object>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Timber.d("Something get Wrong");
}
#Override
public void onNext(Object b) {
Timber.d("Everything is ok");
}
});
}
I tried to chain everything together with flatMap and so on, but it works not correctly in the Chain and also the ErrorHandling was strange, because when i get some Error from the Backend my app will Crash, this is why i add every call the Action3 with OnComplete,OnError,OnNext. Is there a possibility to do this with less code?
Thanks
What have you try with flatMap ?
regarding your code, it seams that you call checkObjectExists then, depending of the result addObjectA then addObjectB or only addObjectB.
So, it can be achieve like this :
ServiceA serviceA= ServiceA.create();
ServiceA serviceB= ServiceB.create();
serviceA.checkObjectExists(id)
.flatMap(exists -> {
if(exists) {
return serviceB.addObjectB(id, b)
} else {
return serviceA.addObjectA(id, a).flatMap(newA -> serviceB.addObject(id, newA);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe();
(as it miss some part of your code, it's hard to undertand from where a, b and other variables come from.
As you can see, it only rely on flatMap and may be closed to what you already try.

How do I change rx.Observable<java.util.list<T>> to java.util.list<T>

Declaration
#GET("api/Game/SearchGames")
Observable<List<GameModel>> searchGames();
This is the network call
public static Observable<List<GameModel>> searchGames () {
VersusAPI client = VersusServiceGenerator.createService(VersusAPI.class);
Observable<List<GameModel>> ob = client.searchGames();
return ob;
}
Here is where I implement.
mAdapterMyGames = new RecyclerViewAdapter(searchGames());
searchGames() returns rx.Observable<java.util.list<GameModel>>. How do I change that to only java.util.list<GameModel>?
You don't properly understand what is an Observable.
It is an object, to which You can subscribe() to get the result of it's operation. Usually, only when subscribing to an Observable it starts and you can get the result inside Subscriber's onNext() function.
So in your case:
Subscribe to this Observable.
Look for the result inside this subscriber's onNext function.
searchGames().subscribe(new new Subscriber<List<GameModel>>() {
#Override
public void onNext(List<GameModel> gameModels) {
//TODO make sth useful with models
}
#Override
public void onCompleted() { }
#Override
public void onError(Throwable e) { }
)

Retrofit + rxJava: how to implement iterable N requests?

I have a problem to implement following problem: I'm making a request that fetches all active leagues. Then, for each of them I need to make another request to grab the matches. I think it's possible to implement the solution using flatMapIterable, but don't know how.
For now I have following retrofit interfaces:
public interface LeaguesApi
{
#GET("/api/get-leagues")
Observable<ArrayList<League>> getLeagues(#Query("active_only") boolean activeOnly);
}
public interface LeagueApi
{
#GET("/api/get-league-fixtures/{leagueId}")
Observable<ArrayList<Match>> getMatchesPlayed(#Path("leagueId") int leagueId, #Query("played") boolean played);
}
Please advise how to iterate through all leagues in order to perform getMatchesPlayed for each of them. Best would be without lambda expressions, since I'm not using them in my project.
Try this code:
leaguesApi // your REST adapter from retrofit
.getLeagues(true) // fetch leagues
.flatMapIterable(leagues -> leagues) //Transform from ArrayList<Liague> to Observable<Liague>
.flatMap(l -> leagueApi.getMatchesPlayed(l.getId(), true))
.subscribe(
(match) -> {/*This is onNext*/},
t -> t.printStackTrace(), //onError
() -> {/*onComplete*/}
);
UPDATE:
Without lambdas:
leaguesApi // your REST adapter from retrofit
.getLeagues(true) // fetch leagues
.flatMapIterable(new Func1<ArrayList<League>, Iterable<?>>() {
#Override
public Iterable<?> call(ArrayList<League> leagues) {
return leagues;
}
}) //Transform from ArrayList<Liague> to Observable<Liague>
.flatMap(new Func1<League, Observable<Match>>() {
#Override
public Observable<Match> call(League l) {
return leagueApi.getMatchesPlayed(l.getId(), true);
}
})
.subscribe(
new Action1<Match>() {
#Override
public void call(Match match) {
//onNext
}
},
new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
//onError
}
},
new Action0() {
#Override
public void call() {
//onComplete
}
}
);
I'd change that API so it reads like this otherwise you lose a lot of the flexibility of streams:
public interface LeaguesApi
{
#GET("/api/get-leagues")
Observable<League> getLeagues(#Query("active_only") boolean activeOnly);
}
public interface LeagueApi
{
#GET("/api/get-league-fixtures/{leagueId}")
Observable<Match> getMatchesPlayed(#Path("leagueId") int leagueId, #Query("played") boolean played);
}
Can you do that?
If not then to get Observable<T> from Observable<ArrayList<T>> you do:
observable.flatMapIterable(
new Func1<ArrayList<T>, ArrayList<T>>() {
#Override
public ArrayList<T> call(ArrayList<T> list) {
return list;
}
});
much nicer to just say observable.flatMapIterable(x -> x) of course.
To get all played matches for all active leagues just do this:
Observable<League> leagues= getLeagues(true);
Observable<Match> matches =
leagues.flatMap( league -> getMatchesPlayed(league.leagueId, true));
or without lambdas (I wish you hadn't asked for that)
Observable<Match> matches = leagues.flatMap(
new Func1<League, Observable<Match>> () {
#Override
public Observable<Match> call(League league) {
return getMatchesPlayed(league.leagueId, true);
}
});

Categories

Resources