Do something after multiple observables have completed - java

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

Related

Returning a Completable/Observables but checking is first - Flatmap?

My Reactive knowledge is very basic and I was wondering what the right way would be if I like to return an observable from a function which is using an observable. I wanna extend the observable which I am calling with a check.
In my example, I think it is a lot of code for not much. I think I would also need to worry about the disposable of the inner observable. Do I?
public Completable updateUserPhotoURL(Uri photoURL, UserProfileChangeRequest profileUpdates) {
return Completable.create(emitter -> {
if (mFirebaseUser == null) {
emitter.onError(new Exception("Firebase User is not initiated"));
}
RxFirebaseUser.updateProfile(mFirebaseUser, profileUpdates).complete()
.subscribe(new DisposableCompletableObserver() {
#Override
public void onComplete() {
emitter.onComplete();
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
emitter.onError(e);
}
});
});
}
What would be the right (more elegant) way of doing so?

RxJava + Retrofit + Realm is doing unlimited get request

I am completely new to rxJava and it's really confusing, I want to make my app offline first and I've decided to use Realm and Retrofit, First I want to get the data from retrofit and then get the data from my remote webservice then, use realm's insertOrUpdate to merge the remote objects with the local one. I'm able to get on this process so far but when I looked into my Network requests on stetho, this method is complete requesting infinite times. Where did I go wrong? Here's the function
public Observable<RealmResults<Event>> all() {
Realm realm = Realm.getDefaultInstance();
return realm.where(Event.class).findAllAsync()
.asObservable()
.filter(new Func1<RealmResults<Event>, Boolean>() {
#Override
public Boolean call(RealmResults<Event> events) {
return events.isLoaded();
}
})
.doOnNext(new Action1<RealmResults<Event>>() {
#Override
public void call(RealmResults<Event> events) {
service.getEvents()
.subscribeOn(Schedulers.io())
.subscribe(new Action1<List<Event>>() {
#Override
public void call(final List<Event> events) {
try(Realm realm = Realm.getDefaultInstance()) {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.insertOrUpdate(events);
}
});
} // auto-close
}
});
}
});
}
and here's the function on my activity, where I use it
private void getEvents() {
Log.i("EVENTSELECTION", "STARTING");
repository.all()
.subscribe(new Subscriber<List<Event>>() {
#Override
public void onCompleted() {
Log.i("EVENTSELECTION", "Task Completed");
swipeRefreshLayout.setRefreshing(false);
}
#Override
public void onError(Throwable e) {
Log.e("EVENTSELECTION", e.getMessage());
swipeRefreshLayout.setRefreshing(false);
e.printStackTrace();
}
#Override
public void onNext(List<Event> events) {
Log.i("EVENTSELECTION", String.valueOf(events.size()));
}
});
}
Thank you so much.
Where did I go wrong?
Let's go through it:
1.
public Observable<RealmResults<Event>> all() {
Realm realm = Realm.getDefaultInstance();
This opens a Realm instance that will never be closed. So your Realm lifecycle management is wrong, refer to the documentation for best practices.
2.
return realm.where(Event.class).findAllAsync()
.asObservable() // <-- listens for changes in the Realm
// ...
.doOnNext(new Action1<RealmResults<Event>>() {
#Override
public void call(RealmResults<Event> events) {
service.getEvents() // <-- downloads data
.subscribeOn(Schedulers.io())
.subscribe(new Action1<List<Event>>() {
You basically say that "in case there are any changes made to data in Realm, then download data from the service and write it into the Realm"
Which will trigger the RealmChangeListener which will trigger a download and so on.
This is a conceptual error, you're using Realm notifications incorrectly.
RealmResults<T> is not just a list of objects, it is also a subscription for changes. So you need to keep it as a field reference, and "stay subscribed to changes in the database".
RealmResults<Sth> results;
RealmChangeListener<RealmResults<Sth>> changeListener = (element) -> {
if(element.isLoaded()) {
adapter.updateData(element);
}
};
void sth() {
results = realm.where(Sth.class).findAllSortedAsync("id");
results.addChangeListener(changeListener);
}
void unsth() {
if(results != null && results.isValid()) {
results.removeChangeListener(changeListener);
results = null;
}
}
In your case, RealmResults<T> which symbolizes a subscription and also provides access to the current/new data is wrapped as an Observable<T> which you can create subscribers to.
Observable<List<<Sth>> results;
Subscription subscription;
Action1<List<Sth>> changeListener = (element) -> {
if(element.isLoaded()) {
adapter.updateData(element);
}
};
void sth() {
results = realm.where(Sth.class).findAllSortedAsync("id").asObservable();
subscription = results.subscribe(changeListener);
}
void unsth() {
if(subscription != null && !subscription.isUnsubscribed()) {
subscription.unsubscribe();
subscription = null;
results = null;
}
}
As you can see, you have a subscription at the start of the component, and an unsubscription at the end of the component.
Calling Observable.first() is incorrect, it does not make sense to do that. If you saw it in any tutorial (I've seen it before...), then that tutorial was wrong.
So it's really a by design on realm and it won't call the onCompleted, I added a .first() at the end of my getEvents function to get only the first result.

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.

RxJava - Nested Observables? (Retrofit)

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.

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