rxjava run multiple observable sequence with condition - android

i have 4 observable, and i want call it sequence
Observable1 //return boolean
-> Observable2 // if(Observable1 result == true) { call Observable3 } else { call observable4 }
-> Observable3 // call Observable4
-> Observable4
-> subscribe();
i tried it, but when Observable1 result is false onCompleted called
Observable
.fromCallable(new Callable<Boolean>() {
#Override
public Boolean call() throws Exception {
return false;
}
})
.flatMap(new Func1<Boolean, Observable<String>>() {
#Override
public Observable<String> call(Boolean aBoolean) {
if(aBoolean) {
return Observable.just("to Observable2");
}
return null; // to Observable 3
}
})
.flatMap(new Func1<String, Observable<Integer>>() {
#Override
public Observable<Integer> call(String s) {
return Observable.just(1); //to Observable3
}
})
.flatMap(new Func1<Integer, Observable<Boolean>>() {
#Override
public Observable<Boolean> call(Integer integer) {
return Observable.just(true); //success
}
})
.subscribe(new Subscriber<Boolean>() {
#Override
public void onCompleted() {
Log.e("TAG", "onCompleted");
}
#Override
public void onError(Throwable e) {
Log.e("TAG", "onError", e);
}
#Override
public void onNext(Boolean bool) {
Log.e("TAG", "onNext");
}
});
how can achieve to this ?
thanks

There are many ways of doing this. Only proposed this based on the first pseudocode you are offering.
Observable.just(boolean)
.flatmap( boolean -> if (boolean) return Observable3
else return Observable.just(boolean))
.flatmap(boolean -> Observable4)
.subscribe()

i solved this problem with define observables as functions
public void run() {
Observable
.fromCallable(() -> false)
.flatMap(bool -> {
if(bool) {
return getObservable2();
}
return getObservable3();
})
.subscribe(bool -> {
Log.e("TAG", "onNext");
}, e -> {
Log.e("TAG", "onError", e);
}, () -> {
Log.e("TAG", "onCompleted");
});
}
private Observable<Boolean> getObservable2() {
return Observable
.just("test")
.flatMap(s -> getObservable4());
}
private Observable<Boolean> getObservable3() {
return Observable
.just(1)
.flatMap(s -> getObservable4());
}
private Observable<Boolean> getObservable4() {
return Observable.just(true);
}

Observable.just(boolean)
.flatmap( boolean -> if (boolean)
return Observable3.flatmap(observable4)
else return Observable4))
.subscribe()
if the result of first observable is true than obervablr3 is called whose result is then flat mapped into the observable4 second case is if the result of first observable is false then it will call observable4 directly and the result is subscribes in first observable only

This is how I would attempt to solve your problem but it doesn't use flatmap.
Observable2.subscribe( aBoolean -> if(aBoolean){
Observable3.subscribe();
} else {
Observable2.subscribe( result -> Observable3.subscribe());
}
You should never return null from an Observable

Related

From Completable to Observable

I will try to be more clear I can.
I want to loop the elements inside a list, for each element in the MAIN_LIST, start an elaboration. The elaboration consists in another list, SECOND_LIST, that has to be looped. When all the elaboration on each item of the SECOND_LIST is finished, start do the same with the next element in the MAIN_LIST.
When finish elaborating all elements in the MAIN_LIST return complete.
Here is I try to achieve this, but I think there is a better way.
Thanks for your help!
Method that loop the MAIN_LIST
public Completable checkGroupExpiration(List<CheckVersion.ServiceStatus> serviceStatusList) {
return Completable.create(emitter -> {
Observable.fromIterable(serviceStatusList)
.concatMapCompletable(serviceStatus -> {
return checkGroupExpiration(serviceStatus.service, serviceStatus.lastUpdate);
}).subscribe(new CompletableObserver() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onComplete() {
if (!emitter.isDisposed())
emitter.onComplete();
}
#Override
public void onError(Throwable e) {
if (!emitter.isDisposed())
emitter.onComplete();
}
});
});
}
Method that loop SECOND_LIST
protected Completable checkGroupExpiration(String group, long ttl) {
return Completable.create(emitter -> {
readFile(MASTER_NOTE)
.map(s -> {
return new Gson().fromJson(s, MasterNote.class);
}).flatMapObservable(masterNote -> {
return Observable.fromIterable(masterNote.savedFiles.entrySet());
}).filter(stringCacheInfoEntry -> {
return stringCacheInfoEntry.getValue().group.equals(group) && stringCacheInfoEntry.getValue().ttl < ttl;
}).concatMapCompletable(stringCacheInfoEntry -> {
return getFile(stringCacheInfoEntry.getKey(), false)
.doOnSuccess(file -> {
String fileName = file.getName();
file.delete();
Log.d(TAG, "File deleted => " + fileName + " from group => " + group);
}).ignoreElement();
}).subscribe(new CompletableObserver() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onComplete() {
if (!emitter.isDisposed())
emitter.onComplete();
}
#Override
public void onError(Throwable e) {
if (!emitter.isDisposed())
emitter.onComplete();
}
});
});
}
Yes, there is. Don't subscribe to an inner flow inside create but use the flows directly:
public Completable checkGroupExpiration(
List<CheckVersion.ServiceStatus> serviceStatusList) {
retrn Observable.fromIterable(serviceStatusList)
.concatMapCompletable(serviceStatus ->
checkGroupExpiration(serviceStatus.service, serviceStatus.lastUpdate)
)
.ignoreElements();
}
protected Completable checkGroupExpiration(String group, long ttl) {
return
readFile(MASTER_NOTE)
.map(s ->
new Gson().fromJson(s, MasterNote.class)
)
.flatMapObservable(masterNote ->
Observable.fromIterable(masterNote.savedFiles.entrySet())
)
.filter(stringCacheInfoEntry ->
stringCacheInfoEntry.getValue().group.equals(group)
&& stringCacheInfoEntry.getValue().ttl < ttl
)
.concatMapCompletable(stringCacheInfoEntry ->
getFile(stringCacheInfoEntry.getKey(), false)
.doOnSuccess(file -> {
String fileName = file.getName();
file.delete();
Log.d(TAG, "File deleted => " + fileName + " from group => " + group);
})
.ignoreElement()
);
}

How do I throw an exception in an Observable?

I'm trying to validate data so that I can throw an exception that will be specifically handled by the subscriber's onError, but I can't figure out how to throw the exception. This is current attempt:
Realm.getDefaultInstance()
.asObservable()
.map(new Func1<Realm, RealmResults<NewsFeed>>() {
#Override
public RealmResults<NewsFeed> call(Realm realm) {
return realm.where(NewsFeed.class).findAll();
}
})
.flatMap(new Func1<RealmResults<NewsFeed>, Observable<?>>() {
#Override
public Observable<?> call(RealmResults<NewsFeed> newsFeed) {
if(newsFeed.size() == 0) {
// can't do this
return Observable.error(new NoDataException());
}
return newsFeed.first().asObservable();
}
});
This doesn't work because as far as I know, the observable stream must be homogeneous.
Observable on the flatMap func1 should be Observable.
Realm.getDefaultInstance()
.asObservable()
.map(new Func1<Realm, RealmResults<NewsFeed>>() {
#Override
public RealmResults<NewsFeed> call(Realm realm) {
return realm.where(NewsFeed.class).findAll();
}
})
.flatMap(new Func1<RealmResults<NewsFeed>, Observable<NewsFeed>>() {
#Override
public Observable<NewsFeed> call(RealmResults<NewsFeed> newsFeed) {
if(newsFeed.size() == 0) {
return Observable.error(new NoDataException());
}
return newsFeed.first().asObservable();
}
})
You just need to use the plain old Java throw statement there. Exceptions that are not handled within stream operators will be forwarded to the onError block of your subscriber.
Realm.getDefaultInstance()
.asObservable()
.map(new Func1<Realm, RealmResults<NewsFeed>>() {
#Override
public RealmResults<NewsFeed> call(Realm realm) {
return realm.where(NewsFeed.class).findAll();
}
})
.flatMap(new Func1<RealmResults<NewsFeed>, Observable<?>>() {
#Override
public Observable<?> call(RealmResults<NewsFeed> newsFeed) {
if(newsFeed.size() == 0) {
throw new NoDataException();
}
return newsFeed.first().asObservable();
}
});

Limit actions with rxJava and retryWhen operator

My application must do two things in general:
Accept only one network request at the same time
Retry if request failed
That's how I implement it:
public class RequestsLocker {
private volatile boolean isLocked;
public <T> Observable.Transformer<T, T> applyLocker() {
if(!isLocked()) {
return observable -> observable
.doOnSubscribe(() -> {
lockChannel();
})
.doOnUnsubscribe(() -> {
freeChannel();
});
} else {
return observable -> Observable.error(new ChannelBusyException("Channel is busy now."));
}
}
private void lockChannel() {
isLocked = true;
}
private void freeChannel() {
isLocked = false;
}
public boolean isLocked() {
return isLocked;
}
}
Looks nice.
Now my retryWhen implementation:
public static Observable<?> retryWhenAnyIoExceptionWithDelay(Observable<? extends Throwable> observable) {
return observable.flatMap(error -> {
// For IOExceptions, we retry
if (error instanceof IOException) {
return Observable.timer(2, TimeUnit.SECONDS);
}
// For anything else, don't retry
return Observable.error(error);
});
}
There is how I use it:
public Observable<List<QueueCarItem>> finishService(int id, PaymentType paymentType, String notes) {
return carsQueueApi.finishService(id, new FinishCarServiceRequest(paymentType.getName(), notes))
.compose(requestsLocker.applyLocker(RequestsLocker.RequestChannel.CHANGE));
}
...
public void finishCarService(QueueCarItem carItem, PaymentType paymentType,
String notes, Subscriber<List<QueueCarItem>> subscriber) {
queueApiMediator.finishService(carItem.getId(), paymentType, notes)
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.doOnError(this::handleError)
.retryWhen(RxOperatorsHelpers::retryWhenAnyIoExceptionWithDelay)
.subscribe(subscriber);
}
The main problem that doOnUnsubscribe() called on any error and then locker is open for any new request until the timer expires and resubscribing happens again. That's the problem. While the timer is ticking user can make another request.
How I can fix it?
The problem is that you're applying your transformer to the source observable i.e. before your retrywhen.
When there is an error you're always going to unsubscribe from and then resubscribe to the source observable
leading to your doOnUnsubscribe being called.
I suggest you try
public Observable<List<QueueCarItem>> finishService(int id, PaymentType paymentType, String notes) {
return carsQueueApi.finishService(id, new FinishCarServiceRequest(paymentType.getName(), notes));
}
public void finishCarService(QueueCarItem carItem, PaymentType paymentType,
String notes, Subscriber<List<QueueCarItem>> subscriber) {
queueApiMediator.finishService(carItem.getId(), paymentType, notes)
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.doOnError(this::handleError)
.retryWhen(RxOperatorsHelpers::retryWhenAnyIoExceptionWithDelay)
.compose(requestsLocker.applyLocker(RequestsLocker.RequestChannel.CHANGE));
.subscribe(subscriber);
}
PS: The apply locker transformer looks a bit different i.e. it doesn't take an argument in the code you linked.
Using retryWhen, to avoid unsubscribe onError you must use onErrorResumeNext which wont unsubscribe you.
Take a look of this example
/**
* Here we can see how onErrorResumeNext works and emit an item in case that an error occur in the pipeline and an exception is propagated
*/
#Test
public void observableOnErrorResumeNext() {
Subscription subscription = Observable.just(null)
.map(Object::toString)
.doOnError(failure -> System.out.println("Error:" + failure.getCause()))
.retryWhen(errors -> errors.doOnNext(o -> count++)
.flatMap(t -> count > 3 ? Observable.error(t) : Observable.just(null)),
Schedulers.newThread())
.onErrorResumeNext(t -> {
System.out.println("Error after all retries:" + t.getCause());
return Observable.just("I save the world for extinction!");
})
.subscribe(s -> System.out.println(s));
new TestSubscriber((Observer) subscription).awaitTerminalEvent(500, TimeUnit.MILLISECONDS);
}
Also about concurrency, if you do an operation in a flatMap operator, you can specify the Max concurrent.
public final <R> Observable<R> flatMap(Func1<? super T, ? extends Observable<? extends R>> func, int maxConcurrent) {
if (getClass() == ScalarSynchronousObservable.class) {
return ((ScalarSynchronousObservable<T>)this).scalarFlatMap(func);
}
return merge(map(func), maxConcurrent);
}
You can see more examples here https://github.com/politrons/reactive
My current solution is not to unlock RequestLocker on IoException as in this case request will be repeated after delay.
public <T> Observable.Transformer<T, T> applyLocker() {
if(!isLocked()) {
return observable -> observable.doOnSubscribe(() -> {
lockChannel();
}).doOnNext(obj -> {
freeChannel();
}).doOnError(throwable -> {
if(throwable instanceof IOException) {
return; // as any request will be repeated in case of IOException
}
freeChannel(channel);
});
} else {
return observable -> Observable.error(new ChannelBusyException("Channel is busy now"));
}
}

RxSearchView Switch threads exception .Online waiting for

mSubscription = RxSearchView.queryTextChangeEvents(mSearchView)
.subscribeOn(AndroidSchedulers.mainThread())
.debounce(600, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.filter(new Func1<SearchViewQueryTextEvent, Boolean>() {
#Override
public Boolean call(SearchViewQueryTextEvent searchViewQueryTextEvent) {
String query = searchViewQueryTextEvent.queryText().toString();
if (!TextUtils.isEmpty(query) && query.length() >= 11) {
if (!CommonUtil.isMobileNumber(query)) {
PromptManager.getInstance().showToast("please input valid phone");
return false;
}
}
boolean b = !TextUtils.isEmpty(searchViewQueryTextEvent.queryText().toString());
return b;
}
})
.switchMap(new Func1<SearchViewQueryTextEvent, Observable<BaseResponseWrapper<SearchUserResponse>>>() {
#Override
public Observable<BaseResponseWrapper<SearchUserResponse>> call(SearchViewQueryTextEvent searchViewQueryTextEvent) {
// Why run in the main thread here
// 2016/6/12 reset api request
String res = searchViewQueryTextEvent.queryText().toString();
return RetrofitManager.newInstance().getApi().searchUserByPhone(res);
}
})
// switch io thread
.subscribeOn(Schedulers.io())
.map(new Func1<BaseResponseWrapper<SearchUserResponse>, List<SearchUserResponse>>() {
#Override
public List<SearchUserResponse> call(BaseResponseWrapper<SearchUserResponse> fuzzyUserRes) {
// some code here
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<SearchUserResponse>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
LogUtils.w("end thread:" + Thread.currentThread().getName());
LogUtils.w("e :" + e);
**//there throw exception android.os.NetworkOnMainThreadException**
}
#Override
public void onNext(List<SearchUserResponse> fuzzyUsers) {
updateUI(fuzzyUsers);
}
});
When I input to the searchview , onError method will throw android.os.NetworkOnMainThreadException.
I have been in the
After switchmap switch to the IO thread.
I use rxjava version :
'io.reactivex:rxjava:1.1.5'.
what can I do?
You can't call subscribeOn twice in the same stream. Only the first will count.
Your code should look like this:
mSubscription = RxSearchView.queryTextChangeEvents(mSearchView)
.debounce(600, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.filter(new Func1<SearchViewQueryTextEvent, Boolean>() {
#Override
public Boolean call(SearchViewQueryTextEvent searchViewQueryTextEvent) {
String query = searchViewQueryTextEvent.queryText().toString();
if (!TextUtils.isEmpty(query) && query.length() >= 11) {
if (!CommonUtil.isMobileNumber(query)) {
PromptManager.getInstance().showToast("please input valid phone");
return false;
}
}
boolean b = !TextUtils.isEmpty(searchViewQueryTextEvent.queryText().toString());
return b;
}
})
// switch io thread
.observeOn(Schedulers.io())
.switchMap(new Func1<SearchViewQueryTextEvent, Observable<BaseResponseWrapper<SearchUserResponse>>>() {
#Override
public Observable<BaseResponseWrapper<SearchUserResponse>> call(SearchViewQueryTextEvent searchViewQueryTextEvent) {
// Why run in the main thread here
// 2016/6/12 reset api request
String res = searchViewQueryTextEvent.queryText().toString();
return RetrofitManager.newInstance().getApi().searchUserByPhone(res);
}
})
.map(new Func1<BaseResponseWrapper<SearchUserResponse>, List<SearchUserResponse>>() {
#Override
public List<SearchUserResponse> call(BaseResponseWrapper<SearchUserResponse> fuzzyUserRes) {
// some code here
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<SearchUserResponse>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
LogUtils.w("end thread:" + Thread.currentThread().getName());
LogUtils.w("e :" + e);
**//there throw exception android.os.NetworkOnMainThreadException**
}
#Override
public void onNext(List<SearchUserResponse> fuzzyUsers) {
updateUI(fuzzyUsers);
}
});
As long as observeOn is a downstream operator, you should use them to switch over threads many times.
Hope that it helps.

How to manage results in RxJava with Retrofit

I have a Retrofit request API call with RxJava. All works well.
How I can optimise this code. I'm a new one with RxJava.
I would be grateful for a small example code or link.
Thanks.
Observable<Review> observer = Observable.just(review);
observer.subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.newThread())
.map(new Func1<Review, Uri>() {
#Override
public Uri call(Review review) {
Uri uri = null;
/// Some code
return uri;
}
}).subscribe(new Action1<Uri>() {
#Override
public void call(Uri uri) {
if(uri != null) {
Api.Reviews reviewApi = retrofit.create(Api.Reviews.class);
Observable<BaseResponse<Review>> reviews = reviewApi.createReview(authToken, review);
reviews.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map(new Func1<BaseResponse<Review>, Void>(){
#Override
public Void call(BaseResponse<Review> response) {
//Code Here
return null;
}
}).subscribe();
}
}
});
There are a few options: map, doOnNext, flatMap, etc., depending on what do you want to do with the retrofit result afterwards. For example:
reviews
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.map(r -> {
try {
someDB.save(r);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
return r;
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(v -> { }, e -> { log(e); });
or
reviews
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.doOnNext(r -> {
try {
someDB.save(r);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(v -> { }, e -> { log(e); });
or
reviews
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(r -> {
try {
someDB.save(r);
return Observable.empty(); // or Observable.just(r);
} catch (Exception ex) {
return Observable.error(ex);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(v -> { }, e -> { log(e); });
Recently I've published some examples of RxJava usage (https://github.com/dawidgdanski/rx-java-handies). If you find it useful while investigating the API for your cases I'll be glad to help you out.

Categories

Resources