I'm using rx libraries im my app to call some REST api on my server and to show the results on screen.
I'm also following the MVP design pattern. So I have a Presenter and an Interactor classes.
In MainInteractor.java I have the following method:
public Observable<Card> fetchCard(final String clientId, final CardFetchedListener listener) {
Log.i(TAG, "FetchCard method");
// Manipulate the observer
return CARDS
.doOnCompleted(new Action0() {
#Override
public void call() {
Log.d(TAG, "CARDS Completed");
}
})
.flatMap(new Func1<Card, Observable<Card>>() {
#Override
public Observable<Card> call(final Card card) {
return ResourceClient.getInstance(card)
.getIDCard()
.observeOn(AndroidSchedulers.mainThread())
.doOnError(new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
Log.w(TAG, "interactor -> fetchCard 2", throwable);
}
}
})
.flatMap(new Func1<CardMeta, Observable<Card>>() {
#Override
public Observable<Card> call(CardMeta cardMeta) {
card.setCardMeta(cardMeta);
saveOrUpdateCardToTheDb(card);
return Observable.just(card);
}
})
.doOnCompleted(new Action0() {
#Override
public void call() {
Log.d(TAG, "Completed body");
}
});
}
});
}
In the logs I can see the "Completed Body" string.
The above method is being called by MainPresenter.java class as follows:
interactor.fetchCard(clientId, this)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Card>() {
#Override
public void onCompleted() {
Log.i(TAG, "fetchCard onCompleted");
view.hideProgressDialog();
view.updateCardsAdapter(cards);
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "Fetch Card error ", e);
onFailure(parseThrowable(e));
}
#Override
public void onNext(Card card) {
if (card != null) {
Log.i(TAG, card.getTenant() + " was fetched and will be displayed");
}
}
});
The problem is that the onCompleted method in the Presenter class is never bein called. I have tried to call onCompleted myself and it worked, but the problem is I don't know actually when the observable has finished emitting cards.
What am I doing wrong here?
UPDATE
CARDS is also an observable that contains meta info. It is initialized using
Observable.from(tenants)
.filter(...).flatMap(// I'm using create operator here and it is calling its onCompleted method successflly);
Related
I am trying to make webservie call using retrofit and rxjava 2. i was exploring two different approach to use RxJava2. problem is i am getting response whene i use Observable but it is not working with Flowable. Logs are not getting printed when using Flowable i tried to debug it but its not going inside onNext or onComplete or onError. only onSubscribe gets executed.
1) using observable as return type
new WebRequestManager().getContactObservable(userRequest)
.subscribe(new Observer<ResponseData>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(ResponseData responseData) {
Log.e(TAG , "data "+responseData.getStatus());
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
Log.e(TAG , "data complete");
}
}
);
2) Using flowable as return type
new WebRequestManager().getContactFlowable(userRequest)
.subscribe(new Subscriber<ResponseData>() {
#Override
public void onSubscribe(Subscription s) {
Log.e(TAG , "contact subscription ");
}
#Override
public void onNext(ResponseData responses) {
Log.e(TAG , "contact onNext ");
}
#Override
public void onError(Throwable t) {
}
#Override
public void onComplete() {
Log.e(TAG , "contact onComplete ");
}
});
Rest contact retrofit api
public interface ContactApi {
#POST(WebRequest.GET_CONTACTS)
Flowable<ResponseData> getContactFlowable(#Body UserRequest userRequest);
#POST(WebRequest.GET_CONTACTS)
Observable<ResponseData> getContactObservable(#Body UserRequest userRequest);
}
call to webservice
public Flowable<ResponseData> getContactsData(UserRequest userRequest){
return webRequest.getWebClient().create(ContactApi.class).getContacts(userRequest);
}
public Observable<ResponseData> getContact(UserRequest userRequest){
return webRequest.getWebClient().create(ContactApi.class).getContact(userRequest);
}
getting retrofit instance
public static Retrofit getWebClient(){
//if(okHttpClient == null)
okHttpClient = new OkHttpClient.Builder()
.connectTimeout(120,TimeUnit.SECONDS)
.readTimeout(120,TimeUnit.SECONDS)
.writeTimeout(120,TimeUnit.SECONDS)
.addInterceptor(new WebRequestInterceptor("\"application/json\""))
.build();
// if(client == null)
client = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(LoganSquareConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
return client;
}
With Subscribers, you have to call request to get items:
new WebRequestManager().getContactFlowable(userRequest)
.subscribe(new Subscriber<ResponseData>() {
#Override
public void onSubscribe(Subscription s) {
Log.e(TAG , "contact subscription ");
s.request(Long.MAX_VALUE); // <---------------------------------
}
#Override
public void onNext(ResponseData responses) {
Log.e(TAG , "contact onNext ");
}
#Override
public void onError(Throwable t) {
}
#Override
public void onComplete() {
Log.e(TAG , "contact onComplete ");
}
});
See also DisposableSubscriber with its example.
Can anyone tell what the request() method in a DisposableSubscriber does and when it is used? We use it only when you create your own Flowable with Flowable.create? The official documentation says
request(long n):
Requests the specified amount from the upstream if its Subscription is set via onSubscribe already.
But I'm not understanding what does it mean. To try out I made a sample as below
private Flowable<Long> streamOfNums() {
return Flowable.create(e -> {
for (int i = 0; i < 500; i++) {
e.onNext((long) i);
Log.d(TAG, "produced "+i);
}
}, BackpressureStrategy.BUFFER);
}
Consumes it like
streamOfNums()
.subscribeOn(Schedulers.io())
.subscribeWith(new DisposableSubscriber<Long>() {
#Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart: ");
}
#Override
public void onNext(Long aLong) {
Log.d(TAG, "onNext: ");
try {
Log.d(TAG, "consuming data :"+aLong);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
request(4);
}
#Override
public void onError(Throwable t) {
}
#Override
public void onComplete() {
}
});
And what I can see is each time the emitter produces numbers after the delay (2000 ms) given. I have given request(4) but even without that it works in the exact same way.
can anybody explain when what request does and when it is used. Can it be used for pagination scenarios?
request allows the consumer to tell the producer how many elements to produce. By default, DisposableSubscriber requests Long.MAX_VALUE in its onStart() method in which case further request() calls have no effect.
There is rarely the need to actually call request in such end-consumers, but otherwise you could use it to avoid buffer overflow when your end-consumer acts as an asynchronous boundary:
ExecutorService executor = Executors.newSingleThreadedExecutor();
Flowable.range(1, 500)
.doOnNext(v -> Log.d("produced: " + v))
.subscribeOn(Schedulers.io())
.subscribe(new DisposableSubscriber<Long>() {
#Override protected void onStart() {
Log.d(TAG, "onStart: "); // <----- no super.onStart() here!
request(1);
}
#Override public void onNext(Long aLong) {
executor.execute(() -> {
Log.d(TAG, "onNext: ");
try {
Log.d(TAG, "consuming data :"+aLong);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
request(1);
});
}
#Override public void onError(Throwable t) {
executor.execute(() -> t.printStackTrace());
}
#Override public void onComplete() {
executor.execute(() -> Log.d("onComplete"));
}
});
Thread.sleep(100_000);
executor.shutdown();
I have a PublishProcessor which emits values continuously. And I have subscribed the PublishProcessor with two subscribers which observe on two different thread. While the first subscriber continuously receives the data in onNext(T), the second subscriber throws error Error: Could not emit value due to lack of requests after receiving few calls to onNext(T)
Below is my implementation
PublishProcessor<byte[]> publishProcessor = PublishProcessor.create()
dataFlowable.subscribeOn(Schedulers.newThread()).subscribe(publishProcessor);
Subscriber1
publishProcessor.observeOn(Schedulers.newThread())
.subscribeWith(new DisposableSubscriber<byte[]>() {
#Override public void onNext(byte[] bytes) {
//Log.i("Sub1 ", "Data received");
}
#Override public void onError(Throwable t) {
}
#Override public void onComplete() {
Log.i("Record ", "complete");
}
})
Subscriber2
publishProcessor.observeOn(Schedulers.newThread())
.subscribeWith(new DisposableSubscriber<byte[]>() {
#Override public void onNext(byte[] moreData) {
Log.i("Sub2 ", "Data received");
}
#Override public void onError(Throwable t) {
Log.i("Sub2 ", t.getMessage() + " "); // error received after few call to onNext()
}
#Override public void onComplete() {
Log.i("Sub2 ", "complete");
}
})
This is a MissingBackpressureException. It is occurring because the publisher is producing faster than the subscribers can consume. PublishProcessor does not apply backpressure from its downstream subscribers to its upstream sources.
What is dataFlowable? Why not subscribe to it directly?
The first API call returns a list of elements and I then want to subsequently call another API with a String returned in each element of the list from the first API call. I (think I) have got it so that it's calling the second API call with each element of the list but I am unsure how to then subscribe to that to get the results returned from the second call.
discogsService.getSearchResults(searchTerm, mContext.getString(R.string.token))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
// Turns the result into individual elements
.flatMapIterable(RootSearchResponse::getSearchResults)
// I believe this then calls .getRelease() with each ID string
.map(result -> discogsService.getRelease(result.getId()));
Retrofit Interface:
public interface DiscogsService
{
#GET("database/search?")
Observable<RootSearchResponse> getSearchResults(#Query("q") String searchTerm, #Query("token") String token);
#GET("releases/")
Observable<Release> getRelease(#Query("release_id") String releaseId);
}
I'm unsure where to go from here.
I believe .subscribe(...) then gives me the ability to get the Observable<Release> returned from each .getRelease(...). As the above method is called in the Model layer I then need to set up a subscriber in this model layer to pass back to the Presenter and then an additional subscriber in the Presenter to deal with each Observable as the Presenter has access to the View.
Is there a way so that I can just return each Observable from the Model layer so I don't need to have two separate .subscribe(...)s? Or should I use two separate .subscribe(...)s as I can then catch errors on the both of them? I only want the results from the second call.
Here is the full code that I have tried:
In Model:
discogsService.getSearchResults(searchTerm, mContext.getString(R.string.token))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.flatMapIterable(RootSearchResponse::getSearchResults)
.subscribeOn(Schedulers.io())
.map(result -> discogsService.getRelease(result.getId()))
.subscribe(new Observer<Observable<Release>>()
{
#Override
public void onSubscribe(Disposable d)
{
}
#Override
public void onNext(Observable<Release> value)
{
mainPresenter.addToRecyclerView(value);
}
#Override
public void onError(Throwable e)
{
}
#Override
public void onComplete()
{
}
});
In Presenter:
#Override
public void addToRecyclerView(Observable<Release> value)
{
value .observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<Release>()
{
#Override
public void onSubscribe(Disposable d)
{
}
#Override
public void onNext(Release value)
{
Log.e(TAG, "Success! " + value);
results.add(value);
}
#Override
public void onError(Throwable e)
{
Log.e(TAG, "Error: " + e.toString());
Log.e(TAG, "Error: " + e.toString());
}
#Override
public void onComplete()
{
}
});
I would rather expose an Observable<Release> at model level:
Observable<Release> getReleases(...) {
return discogsService.getSearchResults(...)
.flatMapIterable(RootSearchResponse::getSearchResults)
.flatMap(result -> discogsService.getRelease(result.getId()));
}
Presenter would just subscribe to it:
getReleases
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<Release>()
{
#Override
public void onSubscribe(Disposable d)
{
}
#Override
public void onNext(Release value)
{
Log.e(TAG, "Success! " + value);
results.add(value);
}
#Override
public void onError(Throwable e)
{
Log.e(TAG, "Error: " + e.toString());
Log.e(TAG, "Error: " + e.toString());
}
#Override
public void onComplete()
{
}
});
Only one Observable. Note the switch from map() to flatMap() for the second request in getReleases(...). Behind the scene this is where occur the second subscribe.
The final subscribe will receive errors from both requests. I prefer to let the consumer (Presenter) handle errors, because it's the one who care about the response and know what to do in case of errors (displaying a message for example).
It's the one who 'drive' the Observable, who create, dispose it, so it's also his duty to assign thread imho.
Observable make very good contract to expose from one layer to another. It describe the data type, how to consume it and the pattern (Observable ? Single ? Flowable ?).
As an example to getting started with RxAndroid I'm trying to implement a searchbox which triggers a rest call when the users inserts something.
So far I have two working parts. The first observing the EditTextView ...
RxTextView.textChangeEvents(searchEditText)
.debounce(400, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<TextViewTextChangeEvent>() {
#Override
public void onCompleted() {
Timber.d("onCompleted");
}
#Override
public void onError(Throwable e) {
Timber.e(e, "onError");
}
#Override
public void onNext(TextViewTextChangeEvent e) {
Timber.d("onNext" + e.text().toString());
}
});
... and the second part calling the REST API by using a Retrofit Service:
APIManager.getService().searchRestaurants("test")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Restaurant>>() {
#Override
public void onCompleted() {
Timber.d("onCompleted");
}
#Override
public void onError(Throwable e) {
Timber.e(e, "onError");
}
#Override
public void onNext(List<Restaurant> restaurants) {
Timber.d("onNext");
for (Restaurant restaurant : restaurants) {
Timber.d(restaurant.getId() + ": " + restaurant.getName());
}
}
});
My Problem is combining the two parts. I tried by using the flatMap Operator as following:
RxTextView.textChangeEvents(searchEditText)
.debounce(400, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Func1<TextViewTextChangeEvent, Observable<List<Restaurant>>>() {
#Override
public Observable<List<Restaurant>> call(TextViewTextChangeEvent txtChangeEvt) {
return APIManager.getService().searchRestaurants(txtChangeEvt.text().toString());
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Restaurant>>() {
#Override
public void onCompleted() {
Timber.d("onCompleted");
}
#Override
public void onError(Throwable e) {
Timber.e(e, "onError");
}
#Override
public void onNext(List<Restaurant> restaurants) {
Timber.d("onNext");
for (Restaurant restaurant : restaurants) {
Timber.d(restaurant.getId() + ": " + restaurant.getName());
}
}
});
When I do this I get following exception:
java.lang.IllegalStateException: Must be called from the main thread. Was: Thread[RxCachedThreadScheduler-1,5,main]
at com.jakewharton.rxbinding.internal.Preconditions.checkUiThread(Preconditions.java:28)
at com.jakewharton.rxbinding.widget.TextViewTextChangeEventOnSubscribe.call(TextViewTextChangeEventOnSubscribe.java:21)
at com.jakewharton.rxbinding.widget.TextViewTextChangeEventOnSubscribe.call(TextViewTextChangeEventOnSubscribe.java:12)
So I tried to fix that by calling subscribeOn(AndroidSchedulers.mainThread() but in this case, of course, I get an NetworkOnMainThread Exception.
So how Do I do this?
What is a proper way to combine different Observables which should execute on different Threads?
Just remove the first .observeOn(AndroidSchedulers.mainThread()). Take a look at this example
Observable.just(1) // 1 will be emited in the IO thread pool
.subscribeOn(Schedulers.io())
.flatMap(...) // will be in the IO thread pool
.observeOn(Schedulers.computation())
.flatMap(...) // will be executed in the computation thread pool
.observeOn(AndroidSchedulers.mainThread())
.subscribe(); // will be executed in the Android main thread (if you're running your code on Android)