Why does my RxJava Observable not complete? - android

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

Related

How to wait for retrofit response in rxjava

I use retrofit2 with rxjava extension.
I have a list of REST API urls and want to do this:
for each
check whether a corresponding file locally exists
if yes: call the API and store the response or the HTTP error
if not: store a customized error
return the list of those results
My problem is: apply returns (with an empty RequestResult) before the server response is received. I think, I understand why, but I don't know how to fix it, because I need to return a RequestResult and not the Retrofit observable.
How can this be solved?
Here is my code:
#GET
Observable<Response<ResponseBody>> enroll(#Url String url);
class RequestResult {
CustomException error;
Response<ResponseBody> response;
}
Observable<ClassOfListItem> observable = Observable.fromIterable(listOfItems);
observable
.flatMap(new Function<ClassOfListItem, ObservableSource<RequestResult>>() {
#Override
public ObservableSource<RequestResult> apply(ClassOfListItem listItem) throws Exception {
RequestResult requestResult = new RequestResult();
if (fileExists(listItem.url)) {
Observable<Response<ResponseBody>> callObservable = restAPI.enroll(listItem.url)
.subscribeOn(Schedulers.io());
callObservable
.subscribe(new DisposableObserver<Response<ResponseBody>>() {
#Override
public void onNext(Response<ResponseBody> responseBodyResponse) {
onPremiseEnrollmentResult.response = responseBodyResponse;
}
#Override
public void onError(Throwable e) {
onPremiseEnrollmentResult.error = new CustomException(e);
}
#Override
public void onComplete() {
}
});
}
else {
requestResult.error = new CustomException("file not found");
}
return Observable.just(requestResult);
}
}
.toList()
.observerOn(AndroidScheduler.mainThread())
.subscribe(new DisposableSingleObserver<List<RequestResult>>() {
#Override
public void onError(Throwable e) {
Log.d("onError", e.getMessage());
}
#Override
public void onSuccess(List<RequestResult> requestResults) {
// parse results
}
}
)
The flatMap() operator allows you to turn one observable into a different observable. You have a nested observer chain inside your apply() which is not part of the observer chain, so it will be empty because it has not completed yet.
To fix this, when the file exists, return the observable.
observable
.flatMap(new Function<ClassOfListItem, ObservableSource<RequestResult>>() {
#Override
public ObservableSource<RequestResult> apply(ClassOfListItem listItem) throws Exception {
RequestResult requestResult = new RequestResult();
if (fileExists(listItem.url)) {
return restAPI.enroll(listItem.url)
.subscribeOn(Schedulers.io());
}
return Observable.error( new CustomException("file not found") );
}
}
.toList()
.observerOn(AndroidScheduler.mainThread())
.subscribe(new DisposableSingleObserver<List<RequestResult>>() {
#Override
public void onError(Throwable e) {
Log.d("onError", e.getMessage());
}
#Override
public void onSuccess(List<RequestResult> requestResults) {
// parse results
}
}
If you need to capture both errors and successes into the list, then you can add map() operator to wrap RequestResult around the response and onErrorResumeNext() to wrap RequestResult around the error before the toList() operator.
If you are making api call on background thread then what you can do is invoke it synchronously....in your case your retrofit api method would change to following
Call<Response<ResponseBody>> enroll(#Url String url);
and you'd invoke by calling restAPI.enroll(listItem.url).execute()

How can I create a stream in RxJava2 (Android) that has no input value, but produces a String?

In Android, I want to use the call AdvertisingIdClient.getAdvertisingIdInfo(getContext()).getId() on a separate thread (IO-thread) and handle the string on the main thread.
I wan't to do this with RxJava2.
This is what I have now: (which works)
SingleOnSubscribe<String> source = new SingleOnSubscribe<String>() {
#Override
public void subscribe(SingleEmitter<String> e) throws Exception {
e.onSuccess(AdvertisingIdClient.getAdvertisingIdInfo(getContext()).getId());
}
};
Single.create(source)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnError(new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
Timber.e(throwable.getMessage());
}
})
.subscribe(new Consumer<String>() {
#Override
public void accept(String s) throws Exception {
advertisingId = s;
}
});
What I would prefer, which is purely taste, is if I could "just" create the stream and handle it all in the flow of methods. Such as: (warning, super pseudo code)
Completable
.empty()
.produce(() -> String {
return makeString();
})
.sub/obs-On()...
.subscribe(coolString -> {mVariable = coolString})
So, Make an Observable and turn it into an Observable by executing some function.
Just use defer or fromCallable like in this example:
Observable<String> stringObservable = Observable.fromCallable(() -> {
return getStuff();
});
Test
#Test
public void fromCallable() throws Exception {
Observable<String> stringObservable = Observable.fromCallable(() -> {
return getStuff();
});
ExecutorService executorService = Executors.newSingleThreadExecutor(runnable -> {
return new Thread(runnable, "myFancyThread");
});
Scheduler scheduler = Schedulers.from(executorService);
TestObserver<String> test = stringObservable.subscribeOn(scheduler)
.test();
test.await()
.assertResult("wurst");
assertThat(test.lastThread().getName()).contains("myFancyThread");
}
private String getStuff() {
return "wurst";
}

Connecting RxJava Observables in layers

I have 3 layers in my app. Layer1 subscribes to Observable from layer2. Layer2 subscribes to layer3 in order to emit returned data to layer1.
Layer1
layer2.getData(data).subscribe(newData -> {Log.d("onNext", "returned");},
throwable -> {Log.d("onError", throwable.getMessage());});
Suppose layer3 has a method called downloadDataFromApi(data);
public Observable<Data> getData(String data) {
return Observable.create(new Observable.OnSubscribe<Data>() {
#Override
public void call(Subscriber<? super Data> subscriber) {
Data data = new Data();
subscriber.onNext(data);
subscriber.onCompleted();
// Can't find a way to connect to layer3.
}
});
}
What do I need to do in layer2's getData() method? I basically want to have logics before returning Observable back to layer1.
Does that make sense?
Just return the Observable directly. Then layer1 handles subscription as usual.
class Layer2 {
public Observable<Data> getData(String data) {
return layer3.getData(data);
}
}
From what I see you have 3 layers (presentation, business logic, data access).
So what you could do is the following:
class PresentationLayer {
private BusinessLogicLayer layer;
PresentationLayer() {
layer = new BusinessLogicLayer();
}
public void showName() {
layer.getNameWithoutRxPrefix()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
#Override
public void accept(String name) throws Exception {
// show name somewhere
Log.d("PresentationLayer", "name: " + name);
}
});
}
}
class BusinessLogicLayer {
private DataAccessLayer layer;
BusinessLogicLayer() {
layer = new DataAccessLayer();
}
public Observable<String> getNameWithoutRxPrefix() {
return layer.getName()
.map(new Function<String, String>() {
#Override
public String apply(String name) throws Exception {
return name.replace("Rx", "");
}
});
}
}
class DataAccessLayer {
public Observable<String> getName() {
return Observable.just("RxAndroid");
}
}
As you can see, I return an Observable in my data access layer (getName), and chain another method to it in my business logic method (map) before returning it to the presentation layer.

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

Retrofit + RxJava doesn't return anything after network call

So here's my code
RestAdapter restAdapter=new RestAdapter.Builder().setEndpoint(HelperFunctions.BASE_URL).build();
final ApiInterface apiInterface=restAdapter.create(ApiInterface.class);
apiInterface.submitDataToAnalyze("dataToAnalyze", "852741963", 1, uuid, dataToAnalyze.toString(), todayDate)
.onErrorReturn(new Func1<Throwable, BasicResponse>() {
#Override
public BasicResponse call(Throwable throwable) {
Log.e(FILE_NAME, "masuk sini??");
return null;
}
})
.flatMap(new Func1<BasicResponse, Observable<LocationSuggestion>>() {
#Override
public Observable<LocationSuggestion> call(BasicResponse basicResponse) {
Log.v(FILE_NAME, "basicResponse: " + basicResponse.toString());
return Observable.just(null);
}
})
.map(new Func1<LocationSuggestion, Integer>() {
#Override
public Integer call(LocationSuggestion suggestion) {
Log.v(FILE_NAME, "eh sampai sini");
return 1;
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
None of my log was displayed in the logcat. What should I do to know if my network call (and all other subsequant flatmap or map) is working correctly? Btw apiInterface.submitDataToAnalyze() should insert some data into a remote database (which in this case it didn't insert anything, hence I'm trying to figure out where the error happened)

Categories

Resources