RxJava - Nested Observables? (Retrofit) - java

I'm facing the problem that I need an authentication token to create my Retrofit service. I currently use an Observable to obtain said token, causing a rather ugly Observable construct:
Observable<MyService> observable = application.getMyService();
observable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(application.defaultSubscribeScheduler())
.subscribe(new Subscriber<MyService>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "Error creating service: ", e);
}
#Override
public void onNext(MyService myService) {
subscription = myService.searchStuff(searchFor)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(application.defaultSubscribeScheduler())
.subscribe(new Subscriber<AResponseWrapper>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable error) {
Log.e(TAG, "Error loading stuff: ", error);
}
#Override
public void onNext(AResponseWrapper wrapper) {
MainPresenter.this.stuff = wrapper.getStuff();
}
});
}
});
I can't help but feel that this is not how it should be done. Am I right?

The Observable.flatMap is what I was looking for.
It allows mapping the result to another observable:
Observable<MyService> observable = application.getMyService();
subscription = observable
.observeOn(application.defaultSubscribeScheduler())
.subscribeOn(application.defaultSubscribeScheduler())
.flatMap(service -> service.searchStuff(searchFor))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<AResponseWrapper>() {
#Override
public void onCompleted() {
if (series.size() < 1) {
mainView.showMessage(R.string.no_stuff_found);
} else {
mainView.showStuff(stuff);
}
}
#Override
public void onError(Throwable error) {
Log.e(TAG, "Error loading stuff: ", error);
}
#Override
public void onNext(AResponseWrapper wrapper) {
MainPresenter.this.stuff= wrapper.getStuff();
}
});
Note that I first observe on the IO Scheduler and only after the flatMap I'll subscribe on the main thread. Otherwise the service.searchStuff call (at least I think it's that part) would be executed on the Main thread, yielding a NetworkOnMainThreadException.
Thanks to #ahmed-ashraf-g who pointed me to this answer.

Related

SmallRye Mutiny unable to process events asynchronously using subscription

I am developing an application that returns Multi<String>, I would like to make some modifications to it, so I have added some methods, but for some reason it does not enter the next method at all.
My other methods are working absolutely fine. Because I am able to collect it and add it to a List, but I want to do some execution asynchronously, so using this approach.
private final ManagedExecutor managedExecutor;
public void writeTo(StreamingInfo streamingInfo) {
streamingInfo
.getEvents()
.runSubscriptionOn(managedExecutor)
.subscribe()
.withSubscriber(
new Subscriber < String > () {
#Override
public void onSubscribe(Subscription s) {
System.out.println("OnSubscription Method");
System.out.println("ON SUBS END");
}
#Override
public void onNext(String event) {
System.out.println("On Next Method");
}
#Override
public void onError(Throwable t) {
System.out.println("OnError Method");
}
#Override
public void onComplete() {
System.out.println("On Complete Method");
}
});
}
I get the following output:
OnSubscription Method
ON SUBS END
Which means that your subscription is not working for some reason. If I do not add subscription and directly collect to List then everything works as expected. Can anyone suggest what am I doing wrong here?
This is because the underlying Reactive Streams specification that SmallRye Mutiny implements has a built-in backpressure mechanism. The client (in your case your subscriber) needs to request the next item manually from the producer (events) otherwise, no item is sent down the reactive pipeline.
You need to save the Subscription object you receive in the onSubscribe method and call its request(long) method when you can process next item(s):
.withSubscriber(
new Subscriber<String>() {
private Subscription subscription;
#Override
public void onSubscribe(Subscription s) {
System.out.println("OnSubscription Method");
System.out.println("ON SUBS END");
subscription = s;
subscription.request(1);
}
#Override
public void onNext(String event) {
System.out.println("On Next Method");
subscription.request(1);
}
#Override
public void onError(Throwable t) {
System.out.println("OnError Method");
}
#Override
public void onComplete() {
System.out.println("On Complete Method");
}
});
In SmallRye there is also an easier way to do this:
events
.onSubscription()
.invoke(() -> {
System.out.println("OnSubscription Method");
System.out.println("ON SUBS END");
})
.onItem()
.invoke(event -> System.out.println("On Next Method"))
.onFailure()
.invoke(t -> System.out.println("OnError Method"))
.onCompletion()
.invoke(() -> System.out.println("On Complete Method"))
.subscribe()
.with(value -> {});

Why this always runs on main thread?

I tried to implement a very simple example of RxAndroid2. When I try to run this code, it makes me feel confused.
ArrayList<Integer> arr = new ArrayList<>();
arr.add(0);
arr.add(1);
arr.add(2);
arr.add(3);
arr.add(4);
arr.add(5);
arr.add(6);
Observable.fromIterable(arr)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Integer>() {
#Override
public void onSubscribe(Disposable d) {
LogUtil.logD(TAG, Thread.currentThread().getName());
}
#Override
public void onNext(Integer integer) {
LogUtil.logD(TAG, Thread.currentThread().getName());
}
#Override
public void onError(Throwable e) {
LogUtil.logD(TAG, Thread.currentThread().getName());
}
#Override
public void onComplete() {
LogUtil.logD(TAG, Thread.currentThread().getName());
}
});
And the log always tells me that it runs on main thread only.
Any idea around this? It would be very nice, if you can explain me more about observeOn() and subscribeOn().
EDITED: This code still gives me all main thread.
ArrayList<Integer> arr = new ArrayList<>();
arr.add(0);
arr.add(1);
arr.add(2);
arr.add(3);
arr.add(4);
arr.add(5);
arr.add(6);
Observable.fromIterable(arr)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(new Consumer<Disposable>() {
#Override
public void accept(Disposable disposable) throws Exception {
LogUtil.logD(TAG, Thread.currentThread().getName());
}
})
.doOnNext(new Consumer<Integer>() {
#Override
public void accept(Integer integer) throws Exception {
LogUtil.logD(TAG, Thread.currentThread().getName());
}
})
.subscribe(integer -> {
LogUtil.logD(TAG, Thread.currentThread().getName());
});
You have observeOn(AndroidSchedulers.mainThread())
And the javadoc for observeOn() clearly states:
Modifies an ObservableSource to perform its emissions and notifications on a specified Scheduler, asynchronously with an unbounded buffer with Flowable.bufferSize() "island size".
So why are you surprised that things happen on the main thread?!
And just to be clear about this: your code configures the Observable to use the main thread.
Looking further into the related APIs, I am guessing you have to call start() on the your scheduler, like:
Scheduler newThreadScheduler = Schedulers.newThread();
newThreadScheduler.start();
Observable.fromIterable(arr)
.subscribeOn(newThreadScheduler)
.observeOn(newThreadScheduler)
You can move the operators around:
Observable.fromIterable(arr)
.subscribeOn(Schedulers.newThread())
// <------------------------
.doOnSubscribe(new Consumer<Disposable>() {
#Override
public void accept(Disposable disposable) throws Exception {
LogUtil.logD(TAG, Thread.currentThread().getName());
}
})
.doOnNext(new Consumer<Integer>() {
#Override
public void accept(Integer integer) throws Exception {
LogUtil.logD(TAG, Thread.currentThread().getName());
}
})
.observeOn(AndroidSchedulers.mainThread()) // <--------------------------
.subscribe(integer -> {
LogUtil.logD(TAG, Thread.currentThread().getName());
});
This no longer executes the code in doOnSubscibe and doOnNext on the main thread, only the subscribe handler gets executed on the main thread.

Multiple requests using Retrofit and RXJava

I have a service called verifyData. I need call this service N number of times.
Retrofit 2
RXJava
Service
#FormUrlEncoded
#POST("verifyData")
Observable<Adeudos> Adeudos(
#Field("id") int id_user
);
Simple Call
Observable<Adeudos> respuesta = services.verifyData(1);
respuesta.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Adeudos>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Adeudos adeudos) {
}
});
I need execute this "method" usign this array
List<String> ids = new ArrayList(); // 1,2,3,4,5,6,7
SOLUTION
Add retrolambda in my gradle
Java
Observable.from(ids)
.flatMap(s -> services.verifyData(ids).subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Adeudos>() {
#Override
public void onCompleted() {
Log.e("Completed :"," Completed\n");
}
#Override
public void onError(Throwable e) {
Log.e("SplashInteractorImpl : ",e.getMessage()+"\n");
}
#Override
public void onNext(Adeudos adeudos) {
Log.e("SplashInteractorImpl : ",adeudos.getMessage()+"\n");
}
});
If you want to make sequential calls, use .concatMap
Observable.from(ids)
.concatMap(s -> services.verifyData(Integer.parseInt(s)))
//subscription
If you want parallel calls, use a flatMap() with a maxConcurrent parameter
Observable.from(ids)
.flatMap(s -> services.verifyData(Integer.parseInt(s))
.subscribeOn(Schedulers.io()), maxConcurrent)
//subscription
So it creates stream of id from your list and modifies it to make your api calls.

Do something after multiple observables have completed

I'm using RXJava on Android and trying to chain together multiple API calls and do something after both API calls have finished. My API calls all look similar to the code sample provided. Basically make the API call, write each record to the DB in onNext, and after all records have been written, update some cache. I want to fire off both of these calls asynchronously and then after both have hit onCompleted, then do something else. What's the proper way in RX to do this? I don't think I need zip as I don't need to tie together the different streams. I was thinking maybe merge, but my two API calls return a different type of Observable. Please let me know. Thanks.
getUsers()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(Observable::from)
.subscribe(new Subscriber<User>() {
#Override
public void onCompleted() {
updateUserCache();
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "Error loading users", e);
}
#Override
public void onNext(User user) {
insertUserToDB(user);
}
});
getLocations()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(Observable::from)
.subscribe(new Subscriber<Location>() {
#Override
public void onCompleted() {
updateLocationCache();
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "Error loading Locations", e);
}
#Override
public void onNext(Location location) {
insertLocationToDB(location);
}
});
You are thinking correctly. You should use zip operator.
Every one of your function should make a call, write to database and do everything you need. Theat zip output function differently: when it is invoked, you can be sure all Observable's has completed successfully -> just complete your reactive stream.
Create a list of Observable:
List<Observable<?>> observableList = new ArrayList<>();
observableList.add(
getUsers()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(Observable::from)
.insertUserToDB(user)
.toList());
observableList.add(
getLocations()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(Observable::from)
.insertLocationToDB(location)
.toList());
Then zip alll Observable's:
Observable.zip(observableList, new FuncN<Object, Observable<?>>() {
#Override
public Observable<?> call(Object... args) {
return Observable.empty();
}
}).subscribe(new Subscriber<Object>() {
#Override
public void onCompleted() {
updateUserCache();
updateLocationCache();
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Object o) {
}
});
This is pseudocode, but I hope You understand the idea.
In case someone needs it, here's the code I used based on R. Zagórski suggestion:
List<Observable<?>> observableList = new ArrayList<>();
observableList.add(
getUsers()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(Observable::from)
.doOnNext(user->insertUser(user))
.toList()
);
observableList.add(
getLocations()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(Observable::from)
.doOnNext(location->insertLocation(location))
.toList()
);
Observable.zip(observableList, new FuncN<Object>() {
#Override
public Observable<?> call(Object...args) {
return Observable.empty();
}).subscribe(new Subscriber<Object>() {
#Override
public void onCompleted() {
updateUserCache();
updateLocationCache();
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Object o) {
}
});
.zip() is the proper way to do it
you may want to make Retrofit return Single instead of Observable though

How can I reuse a Subscriber between two Observables (RxJava)

In order to not repeat myself, I want to re-use a Subscriber variable between two observables. How do you do accomplish this? My current code below does not work, because after the subscriber is used once, it is unsubscribed and no longer works again. If I new a Subscriber instead of reusing a variable, my subscription works. I don't want to write the same code twice, if possible.
public class HomePresenter extends BasePresenter<HomeView> {
ArticleRepo articleRepo;
#Inject
public HomePresenter(ArticleRepo articleRepo) {
this.articleRepo = articleRepo;
}
#Override
public void onCreate(#Nullable PresenterBundle bundle) {
super.onCreate(bundle);
}
public void onEvent(ArticleCategoryClickedEvent event) {
Timber.v("Adapter position clicked at position: '%d'", event.getAdapterPosition());
view.launchArticleActivity(event.getArticleCategory());
}
public void onEvent(SeabeeOnlineExternalLinkClickedEvent event) {
view.launchExternalLink(event.getSeabeeOnlineExternalLink());
}
public void loadArticleImages() {
articleRepo.getArticleBuckets()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
public void refreshData() {
articleRepo.refreshAndSaveArticles()
.flatMap(new Func1<List<ArticleEntity>, Observable<List<ImageArticleCategoryEntity>>>() {
#Override
public Observable<List<ImageArticleCategoryEntity>> call(List<ArticleEntity> articleEntityList) {
return articleRepo.getArticleBuckets();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
final Subscriber<List<ImageArticleCategoryEntity>> subscriber = new Subscriber<List<ImageArticleCategoryEntity>>() {
#Override
public void onCompleted() {
Timber.v("Loading article images complete!");
view.hideLoadingAnimation();
}
#Override
public void onError(Throwable e) {
Timber.e("Error loading article images", e);
Log.e("tag", "Error loading article images", e);
}
#Override
public void onNext(List<ImageArticleCategoryEntity> integerImageArticleCategoryEntityHashMap) {
view.loadArticleImages(integerImageArticleCategoryEntityHashMap);
}
};
}
A Subscriber should not be reused. It will not work because it is a Subscription and once unsubscribed it is done.
Use an Observer instead if you want to reuse it.
source
You can reuse your subscriber, you just need to create an actual class out of it.
private static class MySubscriber extends Subscriber<List<ImageArticleCategoryEntity>> {...}
Subscriber<> subscriber1 = new MySubscriber();
Subscriber<> subscriber2 = new MySubscriber();
And there you go.

Categories