Is it possible to resubscribe an Observable and get the error?
The Observable<T> retry() method resubscribes the observable but it consumes the error.
final PublishSubject<Integer> observable = PublishSubject.create();
observable
.flatMap(new Func1<Integer, Observable<Integer>>() {
#Override
public Observable<Integer> call(final Integer integer) {
if (integer % 2 == 0) {
return Observable.just(integer);
} else {
return Observable.error(new Exception("int: " + integer));
}
}
})
.retry()
.subscribe(new Action1<Integer>() {
#Override
public void call(final Integer integer) {
Timber.i("integer: %d", integer);
}
},
new Action1<Throwable>() {
#Override
public void call(final Throwable throwable) {
Timber.e(throwable, "throwable");
}
},
new Action0() {
#Override
public void call() {
Timber.w("onCompleted");
}
});
Observable
.range(0, 10)
.delay(2, TimeUnit.MILLISECONDS)
.subscribe(new Action1<Integer>() {
#Override
public void call(final Integer integer) {
observable.onNext(integer);
}
},
new Action1<Throwable>() {
#Override
public void call(final Throwable throwable) {
observable.onError(throwable);
}
},
new Action0() {
#Override
public void call() {
observable.onCompleted();
}
});
The onError part of observable is never called because .retry() consumes the error.
What you're looking for is retryWhen(). This allows you to pass a Func1 which provides you with the Throwable, that means you can place your onError logic there instead.
This is a good article.
Related
I'm not quite understanding the difference between using an observable's doOnNext/doOnSubscribe/doOnComplete versus passing in an observer through the observable's subscribe function. These two seem to do the same thing to me. Can someone give me a use case where I would use one over the other?
Observable.interval(1, TimeUnit.SECONDS)
.take(30)
.map(v -> v + 1)
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(new Consumer<Long>() {
#Override
public void accept(Long aLong) throws Throwable {
// update UI
}
})
Observable.interval(1, TimeUnit.SECONDS)
.take(30)
.map(v -> v + 1)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Long>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
}
#Override
public void onNext(#NonNull Long aLong) {
// update ui
}
#Override
public void onError(#NonNull Throwable e) {
}
#Override
public void onComplete() {
}
})
this is a method written in RxJava
public Observable<String> method() {
return model.getOffers()
.filter(new Func1<Offers, Boolean>() {
#Override
public Boolean call(Offers offers) {
if (offers == null)
return false;
return offers.hasSuperOffer();
}
})
.flatMap(new Func1<Offers, Observable<Long>>() {
#Override
public Observable<Long> call(Offers offers) {
Long offerEndTime = offers.getRemainingTime();
if (offerEndTime == null) {
return Observable.empty();
}
AtomicLong remainingTimeSec;
Long currentTimeSec = System.currentTimeMillis() / 1000;
if (remainingTimeSec.get() == -1 && (offerEndTime > currentTimeSec)) {
remainingTimeSec.set(offerEndTime - currentTimeSec);
} else {
return Observable.empty();
}
return Observable.interval(1, TimeUnit.SECONDS)
.onBackpressureDrop()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.take(remainingTimeSec.intValue())
.doOnUnsubscribe(new Action0() {
#Override
public void call() {
}
})
.doOnCompleted(new Action0() {
#Override
public void call() {
}
})
.map(new Func1<Long, Long>() {
#Override
public Long call(Long elapsedTimeSec) {
return remainingTimeSec.getAndDecrement();
}
});
}
})
.map(new Func1<Long, String>() {
#Override
public String call(Long remainingTime) {
return DateUtils.getRemainingTimeStr(remainingTime);
}
});
}
I am trying to convert it to RxJava3 but some parameters have changed:
Func1 has been changed to Function
Action0 has been changed to Action
After I'm making the changes the following error appears at filter:
filter (io.reactivex.rxjava3.functions#io.reactivex.rxjava3.annotations.NonNull Predicate <? MyClass> in Observable cannot be applied to (anonymous.io.reactivex.rxjava3.functions.Function <MyClass.model.Offers.java.lang.Boolean>)
Can anyone help me?
Thank you!
I want to load the next item(s) when some other observable emits a new item (a trigger).
This is the code that emits the items:
public Observable<Item> get() {
return idApi.get().flatMap(new Function<List<String>, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(#NonNull List<String> ids) throws Exception {
return Observable.fromIterable(ids);
}
}).flatMap(new Function<String, ObservableSource<Item>>() {
#Override
public ObservableSource<Item> apply(#NonNull final String id) throws Exception {
return dataApi.get(id).map(new Function<Data, Item>() {
#Override
public Item apply(#NonNull Data data) throws Exception {
return new Item(data , id);
});
}
});
}
Trigger Observable:
RxView.clicks(view.findViewById(R.id.button_more)).debounce(500, TimeUnit.MILLISECONDS);
The only way I could get around this was using a Subject and holding a reference to a list of ids which wasn't elegant and didn't seem reactive.
Edit: This is my solution so far, but I had to subscribe to trigger event directly. I don't consider it elegant.
#Override
public Observable<Item> get(final Observable<Object> trigger) {
final PublishSubject<Item> subject = PublishSubject.create();
return idApi.get().flatMap(new Function<List<String>, ObservableSource<Queue<String>>>() {
#Override
public ObservableSource<Queue<String>> apply(#NonNull List<String> ids) throws Exception {
final Queue<String> q = new LinkedList<>(ids);
return Observable.just(q);
}
}).flatMap(new Function<Queue<String>, ObservableSource<Item>>() {
#Override
public ObservableSource<Item> apply(#NonNull final Queue<String> ids) throws Exception {
trigger.subscribeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Object>() {
#Override
public void accept(#NonNull Object o) throws Exception {
if (ids.size() > 0) {
final String id = ids.poll();
dataApi.get(id).map(new Function<Data, Item>() {
#Override
public Item apply(#NonNull Data data) throws Exception {
return new Item(data, id) l
}
}).subscribe(new Consumer<Item>() {
#Override
public void accept(#NonNull Item item) throws Exception {
subject.onNext(item);
}
});
} else {
subject.onComplete();
}
}
});
return subject;
}
});
}
Use zip
public Observable<Item> get(View v) {
return idApi.get().flatMap(new Function<List<String>, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(#NonNull List<String> ids) throws Exception {
return Observable.fromIterable(ids);
}
}).zipWith(RxView.clicks(v).debounce(500, TimeUnit.MILLISECONDS), (n, i) -> n))
.flatMap(new Function<String, ObservableSource<Item>>() {
#Override
public ObservableSource<Item> apply(#NonNull final String id) throws Exception {
return dataApi.get(id).map(new Function<Data, Item>() {
#Override
public Item apply(#NonNull Data data) throws Exception {
return new Item(data , id);
});
}
});
}
to get N items with each click
public Observable<Item> getN(View v, int nitems) {
return idApi.get().flatMap(new Function<List<String>, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(#NonNull List<String> ids) throws Exception {
return Observable.fromIterable(ids);
}
}).buffer(nitems).zipWith(RxView.clicks(v).debounce(500, TimeUnit.MILLISECONDS), (n, i) -> n))
.flatMap(new Function<List<String>, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(#NonNull final List<String> ids) throws Exception {
return Observable.from(ids)
}
}
)
.flatMap(new Function<String, ObservableSource<Item>>() {
#Override
public ObservableSource<Item> apply(#NonNull final String id) throws Exception {
return dataApi.get(id).map(new Function<Data, Item>() {
#Override
public Item apply(#NonNull Data data) throws Exception {
return new Item(data , id);
});
}
}
});
}
Edit: You will still have to use subscribeOn to make sure you are on the main thread for RXView.clicks and on the IO thread for any networking.
Not so good but it works:
your get method:
Observable<Item> get(final String id){
return Observable.defer(() -> {
dataApi.get(id).map(new Function<Data, Item>() {
#Override
public Item apply(#NonNull Data data) throws Exception {
return new Item(data , id);
});
})
}
your click:
private List<String> ids = {//your id s}
private int current = 0;
RxView.clicks(view).flatmap(ignore -> get(ids.get(current++)))
.subscribe(//your Observer)
I'd like recommend that answer with zipwith(), seems better than my answer.
I have the following class, I want to return Subscription object or something else so I can cancel the request from where I have referenced subscribe() method , but subscribe(observer) returns void!
How can I do that?
public abstract class MainPresenter<T> {
protected <T> Disposable subscribe(Observable<T> observable, Observer<T> observer) {
observable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
[New Update]
I used this way temporary, I am waiting for better solutions:
protected <T> DisposableMaybeObserver subscribe(final Maybe<T> observable,
final Observer<T> observer) {
return observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableMaybeObserver<T>() {
#Override
public void onSuccess(T t) {
observer.onNext(t);
}
#Override
public void onError(Throwable e) {
observer.onError(e);
}
#Override
public void onComplete() {
observer.onComplete();
}
});
}
[New Update 2]
[![Screenshot][https://i.stack.imgur.com/mioth.jpg]]
[New Update 3]
[]1
You should maybe use use subscribeWith :
private Disposable mDisposable;
public abstract class MainPresenter<T> {
protected Disposable subscribe(Observable<T> observable, DisposableObserver<T> observer) {
mDisposable = observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(observer);
return mDisposable;
}
Then when you need :
if (mDisposable != null && !mDisposable.isDisposed()) {
mDisposable.dispose();
}
Hope this helps.
I have an Observable<MoviesResponse>. My MovieResponse class contains a getResults() methods returning a List<Result>. This Result class has a getTitle() methods returning a String. I want to call the getTitle() methods of all my Result objects to get all the titles of my movies.
I achieved this with the code below using a foreach loop but I think there is a better way to do this by chaining RxJava operators, I just can't figure it out...
Subscription :
Observable<MoviesResponse> moviesResponseObservable = apiService.getTopRatedMoviesObservable(API_KEY);
subscription = moviesResponseObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<MoviesResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(MoviesResponse moviesResponse) {
List<Result> results = moviesResponse.getResults();
for (Result r:results) {
Log.d(LOG_TAG,r.getTitle());
}
}
});
Interface :
public interface ApiService {
#GET("movie/top_rated")
Observable<MoviesResponse> getTopRatedMoviesObservable(#Query("api_key") String apiKey);
}
You can use a flatmap to transform your observable into an Observable<Result> and then use map to turn that into Observable<String>, which you can then subscribe to.
moviesReponseObservable
.subscribeOn(Schedulers.io())
.flatMapIterable(new Function<MoviesResponse, Iterable<Result>>() {
#Override
public Iterable<Result> apply(#NonNull MoviesResponse moviesResponse) throws Exception {
return moviesResponse.getResults();
}
})
.map(new Function<Result, String>() {
#Override
public String apply(#NonNull Result result) throws Exception {
return result.getTitle();
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onNext(String s) {
Log.d("TAG", s);
}
/* ... */
});
I got the following error with #zsmb13 answer :
new Function : map (rx.functions.Func1) in
Observable cannot be applied to (anonymous
java.util.function.Function)reason:
no instance(s) of type variable(s) R exist so that Function conforms to Func1
Anyway this answer was very helpul I just replaced Function with Func1 and used call method.
subscription = moviesResponseObservable
.subscribeOn(Schedulers.io())
.flatMapIterable(new Func1<MoviesResponse, Iterable<Result>>() {
#Override
public Iterable<Result> call(MoviesResponse moviesResponse) {
return moviesResponse.getResults();
}
})
.map(new Func1<Result, String>() {
#Override
public String call(Result result) {
return result.getTitle();
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String s) {
Log.d(LOG_TAG, s);
}
});