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);
}
});
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() {
}
})
I'm creating a list of Observable using a list of values, foreach value a custom Observable. I run them all using merge, but I can't detect which one triggers onNext() or onError()
Like in the code below:
List<Observable<MyHttpRsObj>> observables = new ArrayList<>();
for (String param : paramsList) {
Observable<MyHttpRsObj> objObservable = MyRestClient.get().doHttpRequest(param);
observables.add(fileUploadObservable);
}
Observable<BaseRs> combinedObservables = Observable.merge(observables);
combinedObservables.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<MyHttpRsObj>() {
#Override
public void onCompleted() {
//called only once when all Observables finished
}
#Override
public void onError(Throwable throwable) {
//how to know which Observable has error (which param)
}
#Override
public void onNext(MyHttpRsObj myHttpRsObj) {
//how to know which Observable has sccess (which param)
}
});
It is impossible to know which obsevable triggered the error since you merge all observables into single one.
your best bet is to use one observer for each observable. And a last one for merged Observable.
Like this:
List<Observable<MyHttpRsObj>> observables = new ArrayList<>();
for (String param : paramsList) {
//change to connectable Observable
ConnectableObservable<MyHttpRsObj> objObservable = MyRestClient.get()
.doHttpRequest(param)
.publish();
//don't forget to connect
observable.connect();
observables.add(fileUploadObservable);
//subscribe for each observable
objObservable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<MyHttpRsObj>() {
#Override
public void onCompleted() {
//just partial completed
}
#Override
public void onError(Throwable throwable) {
//you can access param from here
}
#Override
public void onNext(MyHttpRsObj myHttpRsObj) {
//access onNext here
//you can access param from here
}
});
}
Observable<BaseRs> combinedObservables = Observable.merge(observables);
combinedObservables.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<MyHttpRsObj>() {
#Override
public void onCompleted() {
//called only once when all Observables finished
}
#Override
public void onError(Throwable throwable) {
//don't handle error here
}
#Override
public void onNext(MyHttpRsObj myHttpRsObj) {
}
});
PS: use ConnectableObservable to avoid emitting twice
If I'm using retrofit with a rxjava converter to get the response, how and where can I filter the results (into say a List<> of users for example whos property boolean paidDues = true)?
Observable<User> observable = userService.me();
observable.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<User>() {
#Override
public void onCompleted() { // handle completed }
#Override
public void onError(Throwable e) { // handle error }
#Override
public void onNext(User user) { // handle response }
});
Use a Filter:
Observable<User> observable = userService.me();
observable.observeOn(AndroidSchedulers.mainThread())
.filter(new Predicate<User >() {
#Override
public boolean test(#NonNull final User user) throws Exception {
return user.hasPaidDues(); // or something like this
}
})
.subscribe(...);
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.
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.