Correct way to nest RxJava Observables? - java

I'm using RxJava and nesting Observables as below. I want to call one observable inside another observable and have the outer observable emit the result of the inner observable onNext. It appears to work, but I'm not certain this is the correct implementation since I wasn't able to find any documentation to confirm.
public Observable<User> updateUser(final String id) {
return Observable.create(new Observable.OnSubscribe<User>() {
#Override
public void call(final Subscriber<? super User> observer) {
try {
if (!observer.isUnsubscribed()) {
getUser(id).subscribe(new Action1<User>() {
#Override
public void call(User user) {
observer.onNext(user);
observer.onCompleted();
}
});
}
} catch (Exception e) {
observer.onError(e);
}
}
});
}
public Observable<User> getUser(final String id) {
...
}

Avoid calls to subscribe when you are just doing Observable transformations as you have to be mindful of all the problems mentioned by #akarnokd and #zsxwing in the comments.
I would also avoid using Observable.create because creating OnSubscribe implementations involves considering backpressure and consequent tricky business with concurrency. Prefer Observable.just,Observable.from,Observable.range,Observable.defer, Observable.using (there are more, check the wiki) and for more advanced purposes implement SyncOnSubscribe.
This code probably covers your use case:
public Observable<User> updateUser(final String id) {
return getUser(id).doOnNext(user -> updateUser(user));
}
public void updateUser(User user) {
//whatever you want here
}
public Observable<User> getUser(final String id) {
...
}

Related

How I would use SwitchMap (RXJAVA) in my code?

I'm new to Android development and am currently trying to make a simple MVC app that works with Rest API.
API calls are made without using Retrofit, although this is not so important. The main catch is that using Observable with debounce and SwitchMap I still get too many API calls (and the extra ones should be discarded). The function is called when text is entered (EditText Listener with TextWatcher). And when administered continuously without delay word, every symbol processed by the server and should only be administered when not within 600 milliseconds. Please help me.
public Observable<String> getObservable(final String s){
return Observable
.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext(model.translateText(s));
}
});
}
public Observer<String> observer = new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String s) {
mainView.hideProgress();
mainView.showResult(s);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
};
public void onEditTextChange(String textForTranslate){
mainView.showProgress();
getObservable(textForTranslate)
.debounce(600,TimeUnit.MILLISECONDS)
.switchMap(new Function<String, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(String s) throws Exception {
return Observable.just(s);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
You are creating a new observable every time a character is typed. So multiple observers are created with each having separate debounce (time throttle) and switch but they are not reused. Instead you create a new observable whenever text changes and start rx chain on it.
You need to create a single PublishSubject
private final PublishSubject<String> querySubject = PublishSubject.create();
that emits entered text/query whenever text is changed. Use it in your callback:
public void onEditTextChange(String textForTranslate) {
querySubject.onNext(textForTranslate);
}
And in your main function, subscribe to observable:
querySubject
.debounce(600, TimeUnit.MILLISECONDS)
.distinctUntilChanged()
.switchMap(new Function<String, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(String s) throws Exception {
// perform api call or any other operation here
return Observable.just(s);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
Debounce operator emits single item only after given time (600 ms) has passed. It ignores items if current item is being processed and given time has not passed.
distinctUntilChanged helps in reducing processing of same query.

RxJava - How to get Single from nested method

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 */)
}

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 list of `Single` observable

I'm new in RxJava so I still have many doubts.
I'm creating this:
#Override
public Single<Result> saveUser(final User user) {
return Single.create(new Single.OnSubscribe<Result>() {
#Override
public void call(final SingleSubscriber<? super Result> singleSubscriber) {
if(user.isValid()){
save(user);
//Set result ok
singleSubscriber.onSuccess(result);
} else {
//This sets an error.
singleSubscriber.onError(error);
}
}
});
}
Depending of the success of the operation, the Single emits it's data and who is subscribed receives the data.
The problem now is that at some point I need to store a list of users. Something like:
public void saveUsers(List<User> listOfUsers){
for (User user : listOfUsers) {
saveUser(user);
}
}
How can I create an Single so I can be subscribed to the initial Single
I would rather create flatmap out of observable list.
public void saveUsers(List<User> listOfUsers){
Observable.from(listOfUsers).flatMap((User user)->{
if(user.isValid()){
save(user);
//Set result ok
return Observable.just(result);
}else
return Observable.error(new RuntimeException("..."));
}).retry(2);
}
If you make your saveUsers method blocking, call Observable#toBlocking.

Making N sequential api calls using RxJava and Retrofit

I have a list of files that I'd like to upload to the backend from an Android device. Due to memory constraints, I'd like to make the second API call only after the first finished, the third after the second finished, and so on.
I wrote something like
private Observable<Integer> uploadFiles(List<File> files) {
return Observable.create(subscriber -> {
for (int i = 0, size = files.size(); i < size; i++) {
UploadModel uploadModel = new UploadModel(files.get(0));
int uploadResult = retrofitApi.uploadSynchronously(uploadModel);
subscriber.onNext(uploadResult);
}
subscriber.onCompleted();
}).subscribeOn(Schedulers.newThread());
}
But I feel like this might be going against the spirit of Rx, and the saying is if you're using Observable.create, you're probably doing it wrong...
Is this a reasonable approach? Is there a better way to achieve this with Retrofit's RxJava integration?
Naively, I would do that (it does not work, though, see below):
return Observable.from(files).concatMap(file -> retrofitApi.upload(uploadModel));
Now the issue is that there is no way to tell retrofit to use only one thread for those calls.
reduce, however, passes the result of one function call to the next, along with the next emitted value from the original observable. That would work, but the function passed to reduce needs to be synchronous. Not good.
Another approach would be to modify the observable recursively:
void getNextFile(int i) {
return retrofit.upload(i).
onNext(result -> getNextFile(i + 1));
}
roughly. But I am not sure how to clean it to make it more readable.
The cleanest I would think would be something like:
Observable.from(files).map(file -> retrofitApi.uploadSynchronously(new UploadModel(file)));
The natives of RxJava would emit all items in Observable.from(...) as if in parallel. That's the best way to think of it as parallel emission. However some cases require real consequent execution of the whole chain. I've come to the following solution, probably not the best one but working.
import rx.Observable;
import rx.Subscriber;
import java.util.Iterator;
import java.util.function.Function;
public class Rx {
public static void ignore(Object arg) {
}
public static <E, R> Observable<Void> sequential(Iterator<E> iterator, Function<E, Observable<R>> action) {
return Observable.create(collectorSubscriber ->
Observable.<Void>create(producerSubscriber ->
producerSubscriber.setProducer(ignoredCount -> {
if (!iterator.hasNext()) {
producerSubscriber.onCompleted();
return;
}
E model = iterator.next();
action.apply(model)
.subscribe(
Rx::ignore,
producerSubscriber::onError,
() -> producerSubscriber.onNext(null));
}))
.subscribe(new Subscriber<Void>() {
#Override
public void onStart() {
request(1);
}
#Override
public void onCompleted() {
collectorSubscriber.onNext(null);
collectorSubscriber.onCompleted();
}
#Override
public void onError(Throwable e) {
collectorSubscriber.onError(e);
}
#Override
public void onNext(Void aVoid) {
request(1);
}
}));
}
}
Example usage would be:
Iterator<? extends Model> iterator = models.iterator();
Rx.sequential(iterator, model -> someFunctionReturnsObservable(model))
.subscribe(...);
This method guarantees chained executions of
Observable<Dummy> someFunctionReturnsObservable(Model model)
Currently the prefered way of creating observables is with fromAsync:
Observable.fromAsync(new Action1<AsyncEmitter<Object>>()
{
#Override
public void call(final AsyncEmitter<Object> emitter)
{
emitter.onNext(object);
emitter.onCompleted();
emitter.setCancellation(new AsyncEmitter.Cancellable()
{
#Override
public void cancel() throws Exception
{
// on unSubscribe() callback
}
});
}
}, AsyncEmitter.BackpressureMode.BUFFER);

Categories

Resources