Rx concatWith() return only first Flowable result - android

I have posted all methods they are working separately , but I face issues with the first one, where I concatWith() two flowables
return userFavouriteStores()
.concatWith(userOtherStores())
.doOnNext(new Consumer<List<StoreModel>>() {
#Override
public void accept(#io.reactivex.annotations.NonNull List<StoreModel> storeModels) throws Exception {
Log.i("storeModels", "" + storeModels);
}
})
public Flowable<List<StoreModel>> userFavouriteStores() {
return userStores()
.map(UserStores::favoriteStores)
.flatMap(storeId -> storeDao.storesWithIds(storeId))
.map(stores -> { // TODO Konvert to Kotlin map {}
List<StoreModel> result = new ArrayList<>(stores.size());
for (se.ica.handla.repositories.local.Store store : stores) {
result.add(store.toStoreModel(StoreModel.Source.Favourite));
}
return result;
}); }
public Flowable<List<StoreModel>> userOtherStores() {
return userStores().map(UserStores::otherStores)
.flatMap(storeId -> storeDao.storesWithIds(storeId))
.map(stores -> {
List<StoreModel> result = new ArrayList<>(stores.size());
for (Store store : stores) {
result.add(store.toStoreModel(StoreModel.Source.Other));
}
return result;
});}
updated method :userStores() is used for favorite and other stores ,
private Flowable<UserStores> userStores() {
return apiIcaSeResource
.userStores()
.toFlowable(); }
#GET("user/stores")
Single<UserStores> userStores();

Following the comments follow up, and additional information, you don't have a problem specifically with the concat(), I'm assuming it is work, it's just not the tool for what you want to achieve here.
concat() will not concatenate two lists to a single list, but rathe will first emit all items by first Flowable and only then items emitted by second Flowable (hence you must have onComplete so concat will know when Flowable is end, what I asked in the begining).
in order to combine the lists together, I would suggest to zip both stores Obesrvables (favorites/ others), and then simply combine to list to have single output of combined list.
Besides that, as you pointed out, as both stores Observables comes from userStores(), you will invoke the network request twice, which definitely not necessary. you can solve it using publish(), that will share and multicast the network result to both Observables, resulting with single network request.
to sum it up, I would rather recommend to use Single here, not Flowable as you don't have backpressure consecrations. something like the following implementation:
Observable<List<StoreModel>> publish = userStores()
.toObservable()
.publish(userStores ->
Single.zip(
userFavouriteStores(userStores.singleOrError()),
userOtherStores(userStores.singleOrError()),
(favoriteStores, otherStores) -> {
favoriteStores.addAll(otherStores);
return favoriteStores;
}
)
.toObservable()
);

Related

Android - Data from multiple sources

I'm using retrofit, rxjava and realm to build an application. This is what I'm trying to accomplish:
Load data from local DB and at same time issue network request
First time display a loader only
If data is already there in DB display it and show small loader on somewhere
When network results are ready update the list and also save the results to disk
If network results failed then display a message to use saying that data could be outdated.
I know how to use realm, retrofit properly but its the rxjava part that's confusing. Is there an easy way to do this with rxjava?
This is how the current codebase looks like:
CategoryRepository.java
public Observable<List<Category>> getCategories() {
return getCategoriesFromNetwork()
.observeOn(Schedulers.computation())
.doOnNext(this::saveCategoriesToDisk)
.publish(nwResponse -> Observable.merge(nwResponse, getCategoriesFromDisk().takeUntil(nwResponse)));
}
private Observable<List<Category>> getCategoriesFromNetwork() {
return service.getCategories()
.map(categoryListResponse -> categoryListResponse.getData());
}
private Observable<List<Category>> getCategoriesFromDisk() {
return Observable.just(realm.copyFromRealm(
realm.where(Category.class).findAll()
));
}
ViewModel
categoryRepository.getCategories()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(categories -> {
//Do UI stuff
}, throwable -> {
});
To call both functions at the same time you can use the Zip operator or the combineLatest operator. They are essentially the same, I think the difference is for zip to continue you need two new observables and for combine latest you just need one new observable for it to continue to the lambda.
Do something list this:
public Observable<List<Category>> getCategories() {
return Observable.combineLatest(getCategoriesFromNetwork(), getCategoriesFromDisk(), (categoriesFromNet, categoriesFromDisk) -> {
categoriesFromNet.addAll(categoriesFromDisk);
return categoriesFromNet;
}) // now you have a complete list of your categories to do with what you want

Filtering rx.Observable response into two different subscribers

I'm coding an Android App. I'm using Retrofit with RxJava in my network layer. Basically the issue that I have is that I have an endpoint called /feed. This endpoint returns a collection of articles that I show in a RecyclerView. Some of those articles are Ads articles, some are Events and others are Posts. I'm using a clean architecture, so I have an Interactor per each thing that I want to do. This is how my GetFeedInteractor looks like:
#Override
public void execute(final Callback callback, int offset, int pageSize) {
final String userSipId = mUserSipid.get();
mFeedRepository.getFeed(offset, pageSize)
.subscribe(new DefaultSubscriber<List<BaseFeedItem>>() {
#Override
public void onNext(List<BaseFeedItem> baseFeedItems) {
List<BaseFeedItem> cacheCollection = new ArrayList<BaseFeedItem>();
for (BaseFeedItem baseFeedItem : baseFeedItems) {
if (!(baseFeedItem instanceof AdFeedItem)) {
cacheCollection.add(baseFeedItem);
}
}
mFeedCache.saveFeeds(cacheCollection);
callback.onFeedsFetched(baseFeedItems);
}
});
}
As you can see the Callback that I use to show the items in the UI takes all the three types of Feeds and shows them. But I need to save only the Post articles and the Event articles in a Sqlite Cache. The thing is that I don't like doing that for instanceof inside the Subscriber. Is there some better Rx-way of doing this?
Update:
BTW I use the FeedCache#saveFeed method in several places, but only here is where I can get Ads Articles. So doing the Ad filtering inside the FeedCache#saveFeed is not a good choice.
Update2:
I think that the perfect solution for this would be if I could filter the collection using some of the Rx-methods, and have a Subscriber that handles the raw list, and another that handles the ad-filtered list. Something like that.
So, basically you want to 2 streams out of one stream. One will be the raw stream (for displaying UI), and the other will be a filtered stream (for saving in database). Unfortunately, you cannot have multiple onSubscribes, but we can use the concept of Subjects.
final PublishSubject<BaseFeedItem> subject = PublishSubject.create();
subject.subscribe(o -> {
// here will be emitted all items
// update the UI
});
mFeedRepository.getFeed(offset, pageSize)
.map(item -> {
subject.onNext(item);
return item;
})
.filter(item -> !(item instanceof AdFeedItem))
.subscribe(items -> {
// no `AddFeedItem`s here
// save into database
});

How to execute different retrofit requests inside RxJava's switchMap() operator?

I have a searchBar (an EditText) with four tabs below it (each tab should display different results). I'm using RxJava with RxBinding to listen and react to text changes events, and I'm using switchMap() operator to execute a Retrofit service for each text change emission.
Since user can select any of the four tabs I actually execute the corresponding Retrofit request for that tab.
For each of those Retrofit services I receive a different response object.
How can I handle different return types inside switchMap() since the last one needs a common type for all?
I have already asked a similar question previously but the answer while it works doesn't lets me to consume the data from my subscriber. Or is my approach wrong from the beginning and I should try a different approach ?
Code :
RxTextView.textChangeEvents(searchbar.getEditText())
.debounce(400, TimeUnit.MILLISECONDS)
.filter(new Func1<TextViewTextChangeEvent, Boolean>() {
#Override
public Boolean call(TextViewTextChangeEvent text) {
return (text.text().length() > 2);
}
})
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.switchMap(new Func1<TextViewTextChangeEvent, Observable<Void>>() {
#Override
public Observable<Void> call(TextViewTextChangeEvent textViewTextChangeEvent) {
String searchBarText = textViewTextChangeEvent.text().toString();
switch (visibleTab) {
case TAGS:
presenter.executeSearchPostsByTag(searchBarText, String.valueOf(0));
case PEOPLE:
return presenter.executeSearchPostsByPeople(searchBarText, String.valueOf(0));
case COMPANIES:
return presenter.executeSearchPostsByCompanies(searchBarText, String.valueOf(0));
case JOBS:
return presenter.executeSearchPostsByJobs(searchBarText, String.valueOf(0));
default:
return presenter.executeSearchPostsByTag(searchBarText, String.valueOf(0));
}
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Void>() {
#Override
public void onCompleted() {
Timber.i("ON COMPLETED");
}
#Override
public void onError(Throwable e) {
Timber.i("ON ERROR e : %s", e.getMessage());
}
#Override
public void onNext(Void aVoid) {
Timber.i("ON NEXT");
}
});
In the code above you 'll see that I have return type of Observable but that doesn't works I just added it so you 'll see what I'm doing.
Thing is, do any of the executeSearchPostsBy* methods return a non-empty Observable? If all of their Observables are empty, then you can just tack on .cast(Void.class) to all of them. If they do return non-empty observables but you don't care about the items, then tack on .ignoreElements().cast(Void.class).
If you need to do some processing for anything that is returned, then you should do that in different methods, in their own Observable chains.
If you need to do some processing that is common to all of them, then you need to adjust your model to reflect this, even if it's just wrapper classes.

RxJava + retrofit, get a List and add extra info for each item

I'm playing around with RXJava, retrofit in Android. I'm trying to accomplish the following:
I need to poll periodically a call that give me a Observable> (From here I could did it)
Once I get this list I want to iterate in each Delivery and call another methods that will give me the ETA (so just more info) I want to attach this new info into the delivery and give back the full list with the extra information attached to each item.
I know how to do that without rxjava once I get the list, but I would like to practice.
This is my code so far:
pollDeliveries = Observable.interval(POLLING_INTERVAL, TimeUnit.SECONDS, Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR))
.map(tick -> RestClient.getInstance().getApiService().getDeliveries())
.doOnError(err -> Log.e("MPB", "Error retrieving messages" + err))
.retry()
.subscribe(deliveries -> {
MainApp.getEventBus().postSticky(deliveries);
});
This is giving me a list of deliveries. Now I would like to accomplish the second part.
Hope I been enough clear.
Thanks
Finally I found a nice way to do it.
private void startPolling() {
pollDeliveries = Observable.interval(POLLING_INTERVAL, TimeUnit.SECONDS, Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR))
.flatMap(tick -> getDeliveriesObs())
.doOnError(err -> Log.e("MPB", "Error retrieving messages" + err))
.retry()
.subscribe(this::parseDeliveries, Throwable::printStackTrace);
}
private Observable<List<Delivery>> getDeliveriesObs() {
return RestClient.getInstance().getApiService().getDeliveries()
.flatMap(Observable::from)
.flatMap(this::getETAForDelivery)
.toSortedList((d1, d2) -> {
if (d1.getEta() == null) {
return -1;
}
if (d2.getEta() == null) {
return 1;
}
return d1.getEta().getDuration().getValue() > d2.getEta().getDuration().getValue() ? 1 : -1;
});
}
Let's go step by step.
First we create an Observable that triggers every POLLING_INTERVAL time the method getDeliveriesObs() that will return the final list
We use retrofit to get an Observable of the call
We use flatMap to flattern the resut list and get in the next flatmap a Delivery item, one by one.
Then we get the estimated time of arrival set inside the Delivery object and return it
We sort the list to order by estimated time of arrival.
In case of error we print and retry so the interval does not stop
We subscribe finally to get the list sorted and with ETA inside, then we just return it or whatever you need to do with it.
It's working properly and it's quite nice, I'm starting to like rxjava :)
I haven't spent a lot of time with Java 8 lambdas, but here's an example of mapping each object to a different object, then getting a List<...> out at the other end in plain ol' Java 7:
List<Delivery> deliveries = ...;
Observable.from(deliveries).flatMap(new Func1<Delivery, Observable<ETA>>() {
#Override
public Observable<ETA> call(Delivery delivery) {
// Convert delivery to ETA...
return someEta;
}
})
.toList().subscribe(new Action1<List<ETA>>() {
#Override
public void call(List<ETA> etas) {
}
});
Of course, it'd be nice to take the Retrofit response (presumably an Observable<List<Delivery>>?) and just observe each of those. For that we ideally use something like flatten(), which doesn't appear to be coming to RxJava anytime soon.
To do that, you can instead do something like this (much nicer with lambdas). You'd replace Observable.from(deliveries) in the above example with the following:
apiService.getDeliveries().flatMap(new Func1<List<Delivery>, Observable<Delivery>>() {
#Override
public Observable<Delivery> call(List<Delivery> deliveries) {
return Observable.from(deliveries);
}
}).flatMap(...)

How to wait multiple nested async calls by using of RxJava-Android?

I'm new to RxJava, here's my case,
send request A and will get List<A> back
for each A, send request AA and will get AA back, bind A and AA then
there is B & BB with similar logic
do something only after all requests complete
Example:
request(url1, callback(List<A> listA) {
for (A a : listA) {
request(url2, callback(AA aa) {
a.set(aa);
}
}
}
A and B are independent
How to structure the code? I also used Retrofit as network client.
OK, I think this should solve the first part of your problem:
Notice that the second call to flatMap is given 2 arguments - there is a version of flatMap that not only produces an Observable for each input item but that also take a second function which in turn will combine each item from the resulting Observable with the corresponding input item.
Have a look at the third graphic under this heading to get an intuitive understanding:
https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#flatmap-concatmap-and-flatmapiterable
Observable<A> obeservableOfAs = retrofitClient.getListOfAs()
.flatMap(new Func1<List<A>, Observable<A>>() {
#Override
public Observable<A> call(List<A> listOfAs) {
return Observable.from(listOfAs);
}
)}
.flatMap(new Func1<A, Observable<AA>>() {
#Override
public Observable<AA> call(A someA) {
return retrofitClient.getTheAaForMyA(someA);
}
},
new Func2<A, AA, A>() {
#Override
public A call(A someA, AA theAaforMyA) {
return someA.set(theAaforMyA);
}
})
...
From here on I am still not sure how you want to continue: Are you ready to just subscribe to the resulting Observable of As? That way you could handle each of the As (onNext) or just wait until all are done (onCompleted).
ADDENDUM: To collect all Items into a single List at the end, that is turn your Observable<A> into an Observable<List<A>> use toList().
https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#tolist
So you have:
Observable<List<A>> observableOfListOfAs = observableOfAs.toList();
If you need more fine grained control over the construction of your list, you can also use reduce.
https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#reduce
For the Bs, simply duplicate the whole flow you used for the As.
You can then use zip to wait for both flows to complete:
Observable.zip(
observableOfListOfAs,
observableOfListOfBs,
new Func2<List<A>, List<B>, MyPairOfLists>() {
#Override
public MyPairOfLists call(List<A> as, List<B> bs) {
return new MyPairOfLists(as, bs);
}
}
)
.subscribe(new Subscriber<MyPairOfLists>() {
// onError() and onCompleted() are omitted here
#Override
public void onNext(MyPairOfLists pair) {
// now both the as and the bs are ready to use:
List<A> as = pair.getAs();
List<B> bs = pair.getBs();
// do something here!
}
});
I suppose you can guess the definition of MyPairOfLists.

Categories

Resources