Retrofit and RxJava: Combine Flowable with Observable to get result - android

I want to combine a Flowable with an Observable to create a Model from that sources.
From Room I get CartModel items this models contains an id of product and quantity.
#Override
public Flowable<List<CartModel>> getCartItems() {
return appLocalDataStore.getCartItems();
}
After that I have a function that for every id from above function compose a Pair of quantity and Product Details.
#Override
public Observable<List<Pair<WsProductDetails, Integer>>> getCartWithProductData() {
return appLocalDataStore.getCartItems().toObservable().flatMap(new Function<List<CartModel>, ObservableSource<CartModel>>() {
#Override
public ObservableSource<CartModel> apply(List<CartModel> cartModels) throws Exception {
return Observable.fromIterable(cartModels);
}
}).flatMap(new Function<CartModel, ObservableSource<Pair<WsProductDetails, Integer>>>() {
#Override
public ObservableSource<Pair<WsProductDetails, Integer>> apply(final CartModel cartModel) throws Exception {
return appRemoteDataStore.getProductBySKU(cartModel.getId()).map(new Function<WsProductDetails, Pair<WsProductDetails, Integer>>() {
#Override
public Pair<WsProductDetails, Integer> apply(WsProductDetails wsProductDetails) throws Exception {
Log.d("TestCartItems", "SKU" + wsProductDetails.getSku() + " quantity" + cartModel.getQuantity());
return new Pair<>(wsProductDetails, cartModel.getQuantity());
}
});
}
}).toList().toObservable();
}
#GET(PATH_PRODUCT_DETAILS_BY_SKY)
Observable<WsProductDetails> getProductBySKU(#Path(PATH_CODE) String cod);
Above function works ok , because the logs are corect.
But in My Presenter OnNext()||OnError()||onComplete() function is never called.
Why this happens?
#Override
public void getCartItems() {
Observable<List<Pair<WsProductDetails, Integer>>> source = appDataStore.getCartWithProductData();
mCompositeDisposable.add(source
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribeWith(new DisposableObserver<List<Pair<WsProductDetails, Integer>>>() {
#Override
public void onComplete() {
Log.d(TAG, "onComplete()");
}
#Override
public void onNext(List<Pair<WsProductDetails, Integer>> pairs) {
Log.d(TAG, "onError()");
view.showCartItems(pairs);
}
#Override
public void onError(Throwable e) {
Log.d(TAG, "onError()");
}
}));
}
}

You have to apply the lookup flatMap to the inner fromIterable so it gets finite:
#Override
public Observable<List<Pair<WsProductDetails, Integer>>> getCartWithProductData() {
return appLocalDataStore.getCartItems().toObservable().flatMap(new Function<List<CartModel>, ObservableSource<CartModel>>() {
#Override
public ObservableSource<CartModel> apply(List<CartModel> cartModels) throws Exception {
return Observable.fromIterable(cartModels)
.flatMap(new Function<CartModel, ObservableSource<Pair<WsProductDetails, Integer>>>() {
#Override
public ObservableSource<Pair<WsProductDetails, Integer>> apply(final CartModel cartModel) throws Exception {
return appRemoteDataStore.getProductBySKU(cartModel.getId())
.map(new Function<WsProductDetails, Pair<WsProductDetails, Integer>>() {
#Override
public Pair<WsProductDetails, Integer> apply(WsProductDetails wsProductDetails) throws Exception {
Log.d("TestCartItems", "SKU" + wsProductDetails.getSku() + " quantity" + cartModel.getQuantity());
return new Pair<>(wsProductDetails, cartModel.getQuantity());
}
});
}
}).toList().toObservable();
}
});
}

Related

Observable fromIterable just return one item

My observable.fromIterable is just returning 1 time.
I am trying to return a list of elements like Observable objects. For that I am using "fromIterable". topMoviesRated.getResults() is returning 20 objects, doOnNext is being callin 20 times but the method getResultFromNetwork is just returning the first element.
Presenter:
public void loadData(boolean error) {
subscription = model.result(error)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<MyFilm>() {
#Override
public void onNext(MyFilm object) {
if(view!=null){
view.updateData(object);
}
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
if(view!=null){
view.showSnackbar("Error...");
}
}
#Override
public void onComplete() {
if(view!=null){
view.showSnackbar("Finished...");
}
}
});
}
Model:
public Observable<MyFilm> result(boolean error) {
if (error)
repository.setPagination(1);
else
repository.setPagination(++pagination);
return Observable.zip(repository.getResultFromNetwork(), detailResult(), new BiFunction<Result, Details, MyFilm>() {
#Override
public MyFilm apply(Result result, Details details) {
return new MyFilm(result.getOriginalName(), result.getId().toString(), details);
}
});
}
public Observable<Details> detailResult() {
return Observable.zip(repository.getDetailsData(), repository.getCountryData(), new BiFunction<TVDetails, String, Details>() {
#Override
public Details apply(TVDetails tvDetails, String country) {
return new Details(tvDetails, country);
}
});
}
Repository:
public Observable<Result> getResultFromNetwork() {
Observable<TopMoviesRated> topMoviesRatedObservable = moviesApiService.getTopMovies("es_ES", pagination);
return topMoviesRatedObservable.concatMap(new Function<TopMoviesRated, Observable<Result>>() {
#Override
public Observable<Result> apply(TopMoviesRated topMoviesRated) {
return Observable.fromIterable(topMoviesRated.getResults());
}
}).doOnNext(new Consumer<Result>() {
#Override
public void accept(Result result) {
Log.d("OnNext -->", result.toString());
}
});
}
Check out this image. The first list has 5 items and second only 4. As you see, result of zip operator contains only 4 items.
Yamko is right, zip is not what you need. Try to use Observable.combineLatest instead of it

onNext not called for PublishSubject in android, rxjava?

I am using rxjava 2 and trying to use rxbus for passing a value
rxbus code
public class SeasonTabSelectorBus {
private static SeasonTabSelectorBus instance;
private PublishSubject<Object> subject = PublishSubject.create();
public static SeasonTabSelectorBus instanceOf() {
if (instance == null) {
instance = new SeasonTabSelectorBus();
}
return instance;
}
public void setTab(Object object) {
try {
subject.onNext(object);
subject.onComplete();
} catch (Exception e) {
e.printStackTrace();
}
}
public Observable<Object> getSelectedTab() {
return subject;
}
}
I am setting the value as
SeasonTabSelectorBus.instanceOf().setTab(20);
This is code of my subscription
SeasonTabSelectorBus.instanceOf().getSelectedTab().subscribe(new Observer<Object>(){
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Object o) {
if (o instanceof Integer) {
int seasonSelected =(int) o;
Log.e("season selected",seasonSelected+"");
}
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
Now I am getting the value on the first call, but when I call again with different values, I do not get the callback.
SeasonTabSelectorBus.instanceOf().setTab(40);
SeasonTabSelectorBus.instanceOf().setTab(90);
SeasonTabSelectorBus.instanceOf().setTab(120);
SeasonTabSelectorBus.instanceOf().setTab(290);
You are receiving only the first one because, after publish (subject.onNext(object)), you are calling subject.onComplete(). Just remove that line.

Is it possible to get 2 values in onNext() of subscriber in rxjava android?

I've an observable like this
Observable.zip(observable, extObs, new Func2<List<UserProfile>, ArrayList<Extension>, UserProfile>() {
#Override
public UserProfile call(List<UserProfile> userProfiles, ArrayList<Extension> extensions) {
return userProfiles.get(0);
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).unsubscribeOn(Schedulers.io()).subscribe(new Subscriber<UserProfile>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onNext(UserProfile userProfile) {
profileListener.onProfileSet(userProfile);
}
});
}
I need to pass the ArrayList in the methodprofileListener.onProfileSet(userProfile); as profileListener.onProfileSet(userProfile,extensions);
Is it possible to do so in zip or is there any other methods of rxjava to solve such type of problems?
You have to do exactly what cricket_007 suggested in the comment.
For example like this:
Create a class that would represent combined results of your observables:
class CombinedResults {
public UserProfile userProfile;
public List<Extension> extensions;
public CombinedResults(UserProfile userProfile, List<Extension> extensions) {
this.userProfile = userProfile;
this.extensions = extensions;
}
}
(Alternatively you could use Pair class)
Use an object of CombinedResults (or Pair) in your Observable.zip Func2.
Observable.zip(observable, extObs,
new Func2<List<UserProfile>, ArrayList<Extension>, CombinedResults>() {
#Override
public CombinedResults call(List<UserProfile> userProfiles, ArrayList<Extension> extensions) {
return new CombinedResults(userProfiles.get(0), extensions);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(Schedulers.io())
.subscribe(new Subscriber<CombinedResults>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onNext(CombinedResults combined) {
profileListener.onProfileSet(combined.userProfile, combined.extensions);
}
});

OnCompleted calls not tidy

i'm new in Rx programming (and I'm having a lot of fun so far ^^).
I'm trying to transform a AsyncTask call into an Rx function.
My function :
Get all the installed apps
normalize the labels
sort everything alphabetically
arrange them by group of letter (it was a Multimap(letter, list of apps)) and pass the result to an adapter to display everything.
Here is how I'm doing so far with Rx :
Observable.from(getInstalledApps(getActivity(), false))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map(new Func1<ResolvedActivityInfoWrapper, ResolvedActivityInfoWrapper>() {
#Override
public ResolvedActivityInfoWrapper call(ResolvedActivityInfoWrapper act) {
// Normalize labels
act.setLabel(Normalizer.normalize(act.getLabel(getPackageManager()).replace(String.valueOf((char) 160), "").trim(), Normalizer.Form.NFD).replaceAll("\\p{M}", ""));
return act;
}
})
.toList()
.subscribe(new Observer<List<ResolvedActivityInfoWrapper>>() {
List<ResolvedActivityInfoWrapper> list;
#Override
public void onCompleted() {
Observable.from(list).groupBy(new Func1<ResolvedActivityInfoWrapper, String>() {
#Override
public String call(ResolvedActivityInfoWrapper input) {
//Get groups by letter
String label = input.getLabel(getPackageManager());
if (!TextUtils.isEmpty(label)) {
String firstChar = label.substring(0, 1);
if (pattern.matcher(firstChar).matches()) {
return firstChar.toUpperCase();
}
}
return "#";
}
}).subscribe(this); // implementation below
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<ResolvedActivityInfoWrapper> list) {
Collections.sort(list, new Comparator<ActivityInfoWrapper>() {
#Override
// Sort all the apps in the list, not sure it's a good way to do it
public int compare(ActivityInfoWrapper info1, ActivityInfoWrapper info2) {
return info1.getLabel(getPackageManager()).compareToIgnoreCase(info2.getLabel(getPackageManager()));
}
});
this.list = list;
}
});
Once I groupedBy letters, on complete I subscribe with this :
#Override
public void onCompleted() {
//display the apps
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(GroupedObservable<String, ResolvedActivityInfoWrapper> input) {
//For each list of apps by letter i subscribe with an observer that will handle those apps (observer code below)
input.subscribe(new TestObserver(input.getKey()));
}
Observer :
private class TestObserver implements Observer<ResolvedActivityInfoWrapper> {
List<ResolvedActivityInfoWrapper> list;
String letter;
public TestObserver(String letter) {
list = new ArrayList<>();
this.letter = letter;
}
#Override
public void onCompleted() {
adapter.addData(letter, list);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(ResolvedActivityInfoWrapper input) {
list.add(input);
}
}
Everything works correctly excpets for one problem : the observer's onCompleted are called not in the right order. So I got all my apps, sorted by letter, but the groups are nots displayed in the right order (C first, then Y, then M etc ...).
I guess there are plenty of errors in the code, can you help me with this probleme and maybe understanding how all this works please ?
Thanks
UPDATE :
Following the advices in the commentary section (thanks people), here is what I'm trying after normalizing the labels :
Observable.from(list).groupBy(new Func1<ResolvedActivityInfoWrapper, String>() {
#Override
public String call(ResolvedActivityInfoWrapper input) {
String label = input.getLabel(getPackageManager());
if (!TextUtils.isEmpty(label)) {
String firstChar = label.substring(0, 1);
if (pattern.matcher(firstChar).matches()) {
return firstChar.toUpperCase();
}
}
return "#";
}
})
.toSortedList(new Func2<GroupedObservable<String, ResolvedActivityInfoWrapper>, GroupedObservable<String, ResolvedActivityInfoWrapper>, Integer>() {
#Override
public Integer call(GroupedObservable<String, ResolvedActivityInfoWrapper> obs1, GroupedObservable<String, ResolvedActivityInfoWrapper> obs2) {
return obs1.getKey().compareToIgnoreCase(obs2.getKey());
}
})
.subscribe(new Observer<List<GroupedObservable<String, ResolvedActivityInfoWrapper>>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<GroupedObservable<String, ResolvedActivityInfoWrapper>> input) {
String test = input.get(0).getKey();
}
});
But it never goes into the Compare function.

A way define order of subscribers in RxJava?

I'm looking a way to define order(?) of observers.
#GET("/get_user_msgs")
Observable<PrivateMessagesResponse> getPrivateMessages(#QueryMap Map<String, String> params);
For example I gave a Observable from my Rest API created by Retrofit.
In my ListView I'm observing this Observable.
api.getPrivateMessages(params).subscribe(new Observer());
I also have an API wrapper for my Espresso tests and I'm subscribing to same Observable there. This way observer in API wrapper is called first and only then observer in ListView
is called.
public class IdlingWrapper implements Api, IdlingResource {
....
public IdlingWrapper(Api realApi) {
this.realApi = realApi;
}
...
public Observable<PrivateMessagesResponse> getPrivateMessages(#QueryMap Map<String, String> params); {
counter.incrementAndGet();
return wrapObservable(realApi.getPrivateMessages(params));
}
protected <T> Observable<T> wrapObservable(final Observable<PrivateMessagesResponse> observable) {
//what to do here?
}
}
Is there a way to force some observer to be notified after all others are done? Or something similar in that matter?
Something like
Observable observable = getObservable();
observable.subscribeAsLast(new LastObserver());
observable.subscribe(new ObserverA());
observable.subscribe(new ObserverB());
And so that ObserverA would be notified first, then ObserverB and only then LastObserver.
Or any other approach where I could find out when all registered observers were notified and completed.
I'm not exactly sure what you are trying to do in IdlingWrapper, but I think the current implementation is very fragile.
I think the most important thing that needs to happen is to guarantee the observable can only be called once.
Here is a quick implementation to demonstrate that as well as my implementation of wrapObservable.
public class Test {
private static int counter = 0;
private static final List<Observable<?>> list = Collections.synchronizedList(new ArrayList<>());
protected static <T> Observable<T> wrapObservable(final Observable<T> original) {
// run atleast once???
synchronized (list) {
list.add(original);
}
return Observable.create(new Observable.OnSubscribe<Void>() {
#Override
public void call(Subscriber<? super Void> subscriber) {
synchronized (list) {
counter++;
if (!list.contains(original)) {
subscriber.onError(new Exception("You can only subscribe once!"));
return;
}
list.remove(original);
}
// Sleep to make it easier to see things happening...
try {
Thread.sleep(3000);
} catch (InterruptedException ignored) {
}
subscriber.onCompleted();
}
}).flatMap(new Func1<Void, Observable<? extends T>>() {
#Override
public Observable<? extends T> call(Void o) {
return original;
}
}).finallyDo(new Action0() {
#Override
public void call() {
synchronized (list) {
counter--;
if (list.size() == 0 && counter == 0) {
System.err.println("finally");
}
}
}
});
}
public static void main(String[] args) throws InterruptedException {
for(int i = 0; i < 10; i++) {
// running in io thread for simulating async call.
Observable<String> test = wrapObservable(Observable.from("TEST!!!!!!")).subscribeOn(Schedulers.io());
test.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
System.err.println("completed");
}
#Override
public void onError(Throwable e) {
System.err.println("error");
}
#Override
public void onNext(String s) {
System.err.println("next");
}
});
// example of calling the same observable twice.
test.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
System.err.println("completed");
}
#Override
public void onError(Throwable e) {
System.err.println("error");
}
#Override
public void onNext(String s) {
System.err.println("next");
}
});
}
Thread.sleep(10000);
}
}
It seems, that this worked just fine.
protected <T> Observable<T> wrapObservable(final Observable<T> original) {
return Observable.create(new Observable.OnSubscribeFunc<T>() {
#Override
public Subscription onSubscribe(final Observer<? super T> t1) {
original.subscribe(new Observer<T>() {
#Override
public void onCompleted() {
t1.onCompleted();
uiThreadHandler.post(new Runnable() {
#Override
public void run() {
counter.decrementAndGet();
notifyIdle();
}
});
}
#Override
public void onError(Throwable e) {
t1.onError(e);
uiThreadHandler.post(new Runnable() {
#Override
public void run() {
counter.decrementAndGet();
notifyIdle();
}
});
}
#Override
public void onNext(T args) {
t1.onNext(args);
}
});
return Subscriptions.empty();
}
});
}
If you want to just use built in RxJava methods to order your observers, you can use flatMap and range to turn each item into multiple items each with a priority and then filter on priority. Observers are ordered based on how they filter.
Here's a trivial example:
Observable<Pair<Integer, Object>> shared = RxView.clicks(findViewById(R.id.textView))
.flatMap(c -> Observable.range(0, 2).map(i -> Pair.create(i, c)))
.share();
shared.filter(p -> p.first == 1)
.map(p -> p.second)
.doOnSubscribe(c -> Log.d(TAG, "first subscribed doOnSubscribe"))
.subscribe(c -> Log.d(TAG, "first subscribed onNext"));
shared.filter(p -> p.first == 0)
.map(p -> p.second)
.doOnSubscribe(c -> Log.d(TAG, "second subscribed doOnSubscribe"))
.subscribe(c -> Log.d(TAG, "second subscribed onNext"));
If you are doing this all over the place

Categories

Resources