The code below....
observes two integer streams from an Android ViewPager (pageSelected, scrollState).
combines & filters the streams looking for a certain combination.
if found, executes a network call.
consumes the result of network call.
Questions
1. Is flatmap() / concatmap() the only way to chain Observables for the code below? (To avoid nesting subscribe() call)
2. Is the use of observeOn() to switch threads correct / intuitive in the flat code or is there a better way to do this? (DataTools call is asynchronous)
I found applying the correct threading between Observable, Operator, and Subscriber confusing when using the nested code.
Flat version
public void loadNextAtTest4(final int pagesRemaining) {
ObservableCombineLatest.combineLatest(pageSelectObs, scrollStateObs, new BiFuncOnlyIdlePages()).filter(new Predicate<Integer>() {
#Override
public boolean test(#NonNull Integer page) throws Exception {
return page > -1 && pagesRemaining == page;
}
}).observeOn(Schedulers.io())
.flatMap(new Function<Integer, Observable<List<PageDescriptor>>>() {
#Override
public Observable<List<PageDescriptor>> apply(#NonNull Integer integer) throws Exception {
return DataTools.getNextPageBase(mRedClient, mFpxClient).toObservable();
}
}).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<PageDescriptor>>() {
#Override
public void accept(#NonNull List<PageDescriptor> pageDescriptors) throws Exception {
mCPAdapter.appendDataset(pageDescriptors);
}
});
}
Nested version
public void loadNextAtTest2(final int pagesRemaining) {
ObservableCombineLatest.combineLatest(pageSelectObs, scrollStateObs, new BiFuncOnlyIdlePages()).filter(new Predicate<Integer>() {
#Override
public boolean test(#NonNull Integer page) throws Exception {
return page > -1 && pagesRemaining == page;
}
}).observeOn(Schedulers.io())
.subscribe(new Consumer<Integer>() {
#Override
public void accept(#NonNull Integer page) throws Exception {
DataTools.getNextDataBase(mRedClient, mFpxClient)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<PageDescriptor>>() {
#Override
public void accept(#NonNull List<PageDescriptor> pageDescriptors) throws Exception {
mCPAdapter.appendDataset(pageDescriptors);
}
});
}
});
}
Related
I'm trying to subscribe following function, but functions's subscribe function doesn't receive any data, but error function does. I realized that if I create second subscriber inside of function it doesn't receive data too. I was able to add multiple subscriber to subject when using Rx-JS. Is it same for Rx-Java or is there different behavior, or am I doing something completely wrong?
public static PublishSubject<Boolean> doSomeWork() {
final PublishSubject<Boolean> res = PublishSubject.create();
doSomeOtherWork().subscribe(new Consumer<Boolean>() {
#Override
public void accept(#NonNull Boolean aBoolean) throws Exception {
res.onNext(true);
}
});
res.subscribe(new Consumer<Boolean>() {
#Override
public void accept(#NonNull Boolean bool) throws Exception {
//receives data
}
}, new Consumer<Throwable>() {
#Override
public void accept(#NonNull Throwable throwable) throws Exception {
handleSomeStuff()
.subscribe(new Consumer<Boolean>() {
#Override
public void accept(#NonNull Boolean aBoolean) throws Exception {
res.onNext(aBoolean);
}
});
}
});
return res;
}
The problem here is the order in which you are doing things. You should subscribe to res before you subscribe to doSomeWork() - otherwise if doSomeWork() emits before the res.subscribe(.. line, nobody is around to receive the emission.
I have 2 streams, the first stream is a stream which takes data from database and call onCompleted() after finish taking data. The second stream is a stream that takes live data from server and never call onCompleted(). What I want to do is to create an operator that can do an action if the first stream(upstream) is an empty stream. Here is the sample:
getItemFromDatabase()
.lift(new DoIfEmptyOperator<Item>(new Action0() {
#Override
public void call() {
//Database is empty
System.out.println("Yeay successfully do an action");
}
}))
.concatWith(getItemFromServer()) // -----> intentionally never complete
.subscribe(new Subscriber<StoryItem>() {
#Override
public void onCompleted() {
//dosomething...
}
#Override
public void onError(Throwable e) {
//dosomething...
}
#Override
public void onNext(StoryItem storyItem) {
//dosomething
}
}));
Here is the code of DoIfEmptyOperator:
import rx.Observable;
import rx.Subscriber;
import rx.functions.Action0;
public class DoIfEmptyOperator<T> implements Observable.Operator<T,T>{
private Action0 action;
private boolean isEmpty = true;
public DoIfEmptyOperator(Action0 action) {
this.action = action;
}
#Override
public Subscriber<? super T> call(final Subscriber<? super T> childSubscriber) {
Subscriber<T> parentSubscriber = new Subscriber<T>() {
#Override
public void onCompleted() {
if(isEmpty) {
action.call();
}
childSubscriber.onCompleted();
}
#Override
public void onError(Throwable e) {
childSubscriber.onError(e);
}
#Override
public void onNext(T t) {
isEmpty = false;
childSubscriber.onNext(t);
}
};
childSubscriber.add(parentSubscriber);
return parentSubscriber;
}
}
However the action is never executed because the parentSubscriber onCompleted() is not firing, because the downstream never completed. If I remove
.concatWith(getItemFromServer())
then the action is executed. Any clue about how to solve the problem? I have dived to the source code of Observable.switchIfEmpty() but still have no clue about how it works.
I would advise against creating an operator.
This could be easily done with existing operators like this:
getItemFromDatabase()
.toList()
.flatMap(list -> {
if (list.isEmpty()) {
// side effect here
}
return getItemFromServer();
});
Have you thought about switchIfEmpty()? As an example of the usage of this operator - I have created some code on GitHub at the following link:
https://github.com/rs146/rxjava-simple/blob/master/src/test/java/SwitchIfEmpty.java
switchIfEmpty() is called when no items are emitted.
However, if you want to get items from the api or the db, then you can do something like the following:
Observable.concat(getFromDatabase(), getFromApi()).first();
As long as both getFromDatabase() and getFromApi() return the same Observable Type. This is a common Rx idiom in Android apps. It basically states that if an item's is not emitted from the database, then go fetch the result from the API instead.
I'm using RxJava 2 on a new project (I've been using RxJava 1 for a long time) and I have some problems with using a flatMap (or maybe flatMapSingle?).
There seems to be something I'm missing in the whole concept.
mObjectManager.getAllObjects returns a AsyncProcessor<List<Object>>.
(I replaced the actual Class name with 'Object').
Disposable subscription = mObjectManager.getAllObjects()
.flatMapSingle(new Function<List<Object>, SingleSource<Object>>() {
#Override
public SingleSource<Object > apply(#io.reactivex.annotations.NonNull List<Object> objects) throws Exception {
// TODO WHAT GOES HERE?!
}
}).filter(new Predicate<Object>() {
#Override
public boolean test(#io.reactivex.annotations.NonNull Object object) throws Exception {
return TextUtils.isEmpty(mSearchTerm) || object.name.toLowerCase().contains(mSearchTerm.toLowerCase());
}
}).toSortedList(new Comparator<Object>() {
#Override
public int compare(Object c1, Object c2) {
return c1.name.compareTo(c2.name);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<Object>>() {
#Override
public void accept(#io.reactivex.annotations.NonNull List<Object> objects) throws Exception {
processObjects(objects);
}
});
I'm wondering how I can transform the list to a SingleSource?
How is the flatMap being used in RxJava 2?
Okay, I found the answer after all.
Flowable.fromIterable does the trick!
...
.flatMap(new Function<List<Object>, Publisher< Object >>() {
#Override
public Publisher< Object > apply(#io.reactivex.annotations.NonNull List< Object > objects) throws Exception {
return Flowable.fromIterable(objects);
}
})
I'm using retrofit2 with Rx. I have two API calls. If first call returns empty response with code 400 I need to make second API call, if not then just to show result. I've implemented custom error handling how shown here. Here is my solution:
getResponse1(token)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverErrorImpl<Response1, BaseError>(BaseError.class) {
#Override
public void onNext(Response1 response) {
view.onSuccess(response);
}
#Override
public void onClientError(BaseError response) {
getResponse2(token)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverErrorImpl<Response2, BaseError>(BaseError.class) {
#Override
public void onNext(Response2 response) {
view.onSuccess(response);
view.hideProgress();
}
#Override
public void onError(Throwable throwable) {
super.onError(throwable);
view.hideProgress();
}
});
}
#Override
public void onError(Throwable throwable) {
super.onError(throwable);
view.hideProgress();
}
});
Is it possible to simplify this code that goes inside method onClientError? Is it good solution to like that?
1). To simplify it, it would be better that Response1 and Response2 will extend some base class, so in your chain, you can operate with base class, which can be casted to certain type when needed
So, let's assume, that you have BaseResponse:
public abstract class BaseResponse{
public static int TYPE_RESPONSE_1 = 1;
public static int TYPE_RESPONSE_2 = 2;
public abstract int getType(); //every class MUST override this method
}
Response1 and Response2 should override BaseResponse
2). getResponse1 and getResponse2 should return Observable<BaseResponse>
3). Your target code:
getResponse1(token)
.onErrorResumeNext(new Func1<Throwable, Observable<BaseResponse>>() {
#Override
public Observable<BaseResponse> call(Throwable throwable) {
// I use Retrofit 1.9
// And in Retrofit 1.9 I have class RetrofitError, which may provide me all info about error
// I'm absolutelly sure Retrofit 2 also have such class,
// but it may have another name
if(/* is your target error */){
//cast it tour target error
return getResponse2(token);
}
return Observable.error(throwable);
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverErrorImpl<Response1, BaseError>(BaseError.class) {
#Override
public void onNext(BaseResponse response) {
if(response.getType() == BaseResponse.TYPE_RESPONSE_1){
view.onSuccess(response);
} else {
view.onSuccess(response);
view.hideProgress();
}
}
#Override
public void onError(Throwable throwable) {
super.onError(throwable);
view.hideProgress();
}
});
Jeesh, why does everyone make it so complicated? In practice I've felt that every time I needed to subscribe to an Observable in the subscribe of another, there is an operator that will do it much more cleanly for me:
<T,E extends Throwable> Observable<T>
whenExceptionIs(Class<E> what, Func1<E,Observable<T>> result) {
return t -> {
return what.isInstance(t) ? result.call(t) : Observable.error(t);
};
}
getResponse1(token)
.onErrorResumeNext(whenExceptionIs(BaseError.class, getResponse2(token)))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(view::onSuccess, view::hideProgress, err -> view.hideProgress());
If you have special error handling needs, create a custom Subscriber that handles that, but make sure that error handling in general is
handled in the Observable chain if it can do anything about it (ignore it, retry the calls etc)
propagated downstream.
I have the following code based on an example provided by #a.bertucci here Emit objects for drawing in the UI in a regular interval using RxJava on Android, where I zip an Observable with a Timer. When I trigger the subscription by calling processDelayedItems(), the code [A] in the zipped Observable is executed exactly once and one item is emitted to [B]. I would have expected code [A] to run continuously once triggered and keep emitting items every 1500 msec, but it obviously only runs once here.
private static void processDelayedItems() {
Observable.zip(
Observable.create(new Observable.OnSubscribe<Object>() {
#Override public void call(Subscriber<? super Object> subscriber) {
// [A] this code is only called once
subscriber.OnNext(o)
}
}),
Observable.timer(1500, 1500, TimeUnit.MILLISECONDS), new Func2<Object, Long, Object>() {
#Override public Object call(Object entity, Long aLong) {
return entity;
}
}
)
.subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
#Override public void call(Object entity) {
// ... and accordingly one item is emitted [B]
}
}, new Action1<Throwable>() {
#Override public void call(Throwable throwable) {
throwable.printStackTrace();
}
}, new Action0() {
#Override public void call() {
}
});
}
Can anybody see the problem which I have here? Is it that I need to reference the Observable from outside the function to keep it alive for more time? Is it collected by GC (Android)? Is it a problem that the function is static?
What are the rules for Observables in terms of their livetime? Are there any best practices how longer-running Observables should be referenced and if they can be static at all? In my tests I noticed that it doesn't really matter, but maybe it does here, when a timer is involved.
--
Corrected code [not working yet]:
added repeat()
Observable.zip(
Observable.create(new Observable.OnSubscribe<Object>() {
#Override public void call(Subscriber<? super Object> subscriber) {
// [A] this code is only called once
subscriber.OnNext(o);
subscriber.OnCompleted();
}
}).repeat(Schedulers.newThread()),
Observable.timer(1500, 1500, TimeUnit.MILLISECONDS), new Func2<Object, Long, Object>() {
#Override public Object call(Object entity, Long aLong) {
return entity;
}
}
)
.subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
#Override public void call(Object entity) {
// ... and accordingly one item is emitted [B]
}
}, new Action1<Throwable>() {
#Override public void call(Throwable throwable) {
throwable.printStackTrace();
}
}, new Action0() {
#Override public void call() {
}
});
You need repeat to generate an infinite Observable. E.g.,
Observable.create(new Observable.OnSubscribe<Object>() {
#Override public void call(Subscriber<? super Object> subscriber) {
// [A] this code is only called once
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(o);
}
if (!subscriber.isUnsubscribed()) {
subscriber.onCompleted();
}
}
}).repeat(Schedulers.newThread());
Is it that I need to reference the Observable from outside the function to keep it alive for more time? Is it collected by GC (Android)? Is it a problem that the function is static?
Since you use Schedulers.newThread() and timer, there will be some other Threads which has a reference to your Observable. You don't need more work.
What are the rules for Observables in terms of their livetime? Are there any best practices how longer-running Observables should be referenced and if they can be static at all? In my tests I noticed that it doesn't really matter, but maybe it does here, when a timer is involved.
You're right. It doesn't matter.
In regard to your comment, for simplicity you could do this,
Observable.timer(1500, 1500, TimeUnit.MILLISECONDS)
.flatMap(new Func1<Long, Observable<Object>>() {
#Override
public Observable<Object> call(Long aLong) {
String o = "0";
return Observable.from(o);
}
})
.subscribe(new Action1<Object>() {
#Override
public void call(Object aLong) {
System.out.println(aLong);
}
});
Here you still get the benefits of the timer without the added zip / repeat on top. It's still a bit verbose but it's a bit simpler.