How do I throw an exception in an Observable? - android

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();
}
});

Related

rxjava run multiple observable sequence with condition

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

How repeat retrofit request with new post values

I have token to make requests with it, this token have some lifeTime.
So when I get 401 error, I need to refreshToken with other request.
I want to make it smoothly for user(without any interruptions): if get 401, than make refresh, and after it repeat first request with new available token.
here is my code:
#POST("/refresh_token")
Observable<Auth> refreshToken(#Body BodyAuth body);
#POST("/someLink")
Observable<Answer> someRequest(#Body OtherBody body);
Observable<Answer> makeSomeRequest(){
retutn someRequest(new Body(currentToken)).
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(StatusCodeVerifier())
.retryWhen(RetryWithNewToken(requestManager))
}
public class StatusCodeVerifier<T> implements Observable.Transformer<Response<T>,T> {
#Override
public Observable<T> call(Observable<Response<T>> responseObservable) {
return responseObservable.
flatMap(new Func1<Response<T>, Observable<?>>() {
#Override
public Observable<?> call(Response<T> tResponse) {
if (tResponse.code() == 401){
throw new UnAuthorizedException("custom exception");
} else {
return Observable.just(tResponse);
}
}
});
}
}
public class RetryWithNewToken implements Func1<Observable<Throwable>, Observable<?>> {
private UserDataManager requestManger;
public RetryWithNewToken(RequestManager requestManger){
this.requestManger = requestManger;
}
#Override
public Observable<?> call(final Observable<Throwable> attempts) {
return attempts.flatMap(new Func1<Throwable, Observable<?>>() {
#Override
public Observable<?> call(final Throwable throwableObservable) {
if (throwableObservable instanceof UnAuthorizedException){
return requestManger. refreshToken().subscribe(new Action1<CbAuth>() {
#Override
public void call(TokenData newToken) {
updateToken(newToken);
//here i try to repeat previous request that was failed because of 401 error
Observable.just(attempts);
}
});
}
return Observable.error(throwableObservable);
}
});
}
}
every time after successful refreshToken previous request called, but it is called with invalid (old one) data.
So how can I repeat request with new token data?
It's because of you initialize your someRequest Observable with its arguments only once. And when an error occurs you just resubscribes to it. But you have to poll currentToken on each subscription. It could be done by using Observable.fromCallable operator:
Observable<Answer> makeSomeRequest(){
return Observable.fromCallable(() -> currentToken) // called on each subscription
.flatMap(token -> someRequest(new Body(token)))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(StatusCodeVerifier())
.retryWhen(RetryWithNewToken(requestManager))
}

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.

Why does my RxJava Observable not complete?

I'm trying to test this observable for a login request, but the subscribers onNext and onCompleted methods are never called.
return mDeviceRepository.getDeviceId()
.flatMap(new Func1<String, Observable<LoginResponse>>() {
#Override
public Observable<LoginResponse> call(String deviceId) {
return mAuthRepository.login(deviceId, mUsername, mHashedPassword);
}
})
.flatMap(new Func1<LoginResponse, Observable<LoginResponse>>() {
#Override
public Observable<LoginResponse> call(LoginResponse loginResponse) {
mSessionRepository.storeSession(UserSession.fromLoginReponse(loginResponse));
mUserRepository.storeUser(loginResponse.getUser());
return Observable.just(loginResponse);
}
});
My test looks like this:
loginUseCase.setCredentials("testuser", "testpassword");
when(mockDeviceRepository.getDeviceId()).thenReturn(Observable.just("testdeviceid"));
when(mockAuthRepository.login("testdeviceid", "testuser", "testpassword")).thenReturn(Observable.just(new LoginResponse()));
testSubscriber = new TestSubscriber();
loginUseCase.buildUseCaseObservable().toBlocking().subscribe(testSubscriber);
testSubscriber.assertCompleted();
testSubscriber.assertValueCount(1);
What about assertTerminalEvent() instead of assertCompleted() in TestSubscriber ?
All of your operators operate on same thread, so if TestSubscriber assertions were reached, then exception was thrown somewhere in stream
Try
loginUseCase.buildUseCaseObservable().subscribe(testSubscriber);
PS: If you want side effects, don't do them in a flatMap. Use a side effect operators e.g.
return mDeviceRepository.getDeviceId()
.flatMap(new Func1<String, Observable<LoginResponse>>() {
#Override
public Observable<LoginResponse> call(String deviceId) {
return mAuthRepository.login(deviceId, mUsername, mHashedPassword);
}
})
.doOnNext(new Action1<LoginResponse>() {
#Override
public void call(String loginResponse) {
mSessionRepository.storeSession(UserSession.fromLoginReponse(loginResponse));
mUserRepository.storeUser(loginResponse.getUser());
}
})
.flatMap(new Func1<LoginResponse, Observable<LoginResponse>>() {
#Override
public Observable<LoginResponse> call(LoginResponse loginResponse) {
return Observable.just(loginResponse);
}
});

Realm Observable not finishing when using rx-java amb() or switchIfEmpty()

When using Realm Observable by calling asObservable() on the query, with amb() or switchIfEmpty() cause the realm's observable to not finish its sequence. A work around to this can be done by using Observable.just() instead of Realms asObservable().
I cant figure out if this is caused by my code or a bug in rx-java or Realm.
mSubscription = getRealmObservable(params).switchIfEmpty(getNetworkObservable(params))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
private Observable<model> getNetworkObservable(UrlParams params) {
final api service = NetworkManager.getAPI();
return service.getModel(params.toMap())
.doOnNext(new Action1<RealmList<Model>>() {
#Override
public void call(RealmList<Model> models) {
if (models != null && models.size() > 0) {
mRealm.beginTransaction();
mRealm.copyToRealmOrUpdate(models);
mRealm.commitTransaction();
}
}
})
.flatMap(new Func1<RealmList<Model>, Observable<Model>>() {
#Override
public Observable<Model> call(RealmList<Model> models) {
return Observable.from(models);
}
});
}
private Observable<Model> getRealmObservable(final UrlParams params) {
return Observable.just(mRealm.where(Model.class).findAll())/*.asObservable()*/ <- Using this cause this sequence not to finish
.filter(new Func1<RealmResults<Model>, Boolean>() {
#Override
public Boolean call(RealmResults<Model> models) {
return models != null && models.isValid() && models.size() > 0;
}
})
.flatMap(new Func1<RealmResults<Model>, Observable<Model>>() {
#Override
public Observable<Model> call(RealmResults<Model> models) {
return Observable.from(models);
}
});
}
The observable created by calling asObservable() on RealmResults will never terminate (call onComplete). See the documentation here.

Categories

Resources