I have a ViewPager with two pages namely Popular and All. What I'm trying to achieve is only push items that have popular tag true to Popular whereas push all items to All.
Currently I have a single class which is used in the PagerAdapter and passing in the page type. How do I filter out PublishSubject so that each page only displays necessary items accordingly.
Both my Observer are subscribed to a single PublishSubject, but I
want to filter when emitting.
Please comment if the question is unclear. I'll try my best to relay this problem. Also sorry if it has already been answered since I couldn't find anything relevant.
The code I'm using is this based on this architecture in which I have a Firebase data store FirebaseSubscriptionDataStore which provides the PublishSubject. This is later subscribed to by SubscribeToSubscriptionUpdates in SubscriptionListPresenterImpl
Thanks in advance.
You can basically define two different methods to get Observable (or Flowable) from PublishSubject. First observable will emit all of the items and second one only popular ones:
public class DataStore {
private PublishSubject<DataItem> dataItemPublishSubject = PublishSubject.create();
public Flowable<DataItem> getAllObservable() {
return dataItemPublishSubject.toFlowable(BackpressureStrategy.BUFFER);
}
public Flowable<DataItem> getPopularObservable() {
return dataItemPublishSubject.toFlowable(BackpressureStrategy.BUFFER)
.filter(new Predicate<DataItem>() {
#Override
public boolean test(DataItem dataItem) throws Exception {
return dataItem.popular;
}
});
}
public static class DataItem {
public final boolean popular;
public DataItem(boolean popular) {
this.popular = popular;
}
}
}
In case you don't want to two methods, you can move .filter() operator everywhere within you Rx chain and you might end up with something like this:
dataStore.getAllObservable()
.doOnNext(new Consumer<DataStore.DataItem>() {
#Override
public void accept(DataStore.DataItem dataItem) throws Exception {
pagerAdapter.addDataAll(dataItem);
}
})
.filter(new Predicate<DataStore.DataItem>() {
#Override
public boolean test(DataStore.DataItem dataItem) throws Exception {
return dataItem.popular;
}
})
.doOnNext(new Consumer<DataStore.DataItem>() {
#Override
public void accept(DataStore.DataItem dataItem) throws Exception {
pagerAdapter.addDataPopular(dataItem);
}
})
.subscribe();
Related
I tried googling this but results are all about what's changed between rxjava 1 and 2... less than helpful, lol.
I'm new to Rx Java and am working through it. I'm able to create an observable pretty easily and all subscribers are getting the right calls. I just have a question:
Object myObject = something1;
I create the observable and all subscribers and they get myObject in the onNext() method.
But, somewhere down the line, we do "myObject = something2".
How do I notify the subscribers that myObject has changed?
Here's how I'm creating the observable:
Observable<MyObject> myObservable = Observable.create(new ObservableOnSubscribe<MyObject>() {
#Override
public void subscribe(ObservableEmitter<MyObject> e) throws Exception {
MyObject myObject = someObject1;
e.onNext(myObject);
}
});
Here's how I'm subscribing:
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(mDisposableObserver);
}
private DisposableObserver<MyObject> mDisposableObserver = new DisposableObserver<MyObject>() {
#Override
public void onNext(MyObject myObject) {
// Do UI stuff with 'myObject' variables
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
};
Side Note: If anybody has links or suggestions to really good Rx Java 2 tutorials (like... super beginner through god-status), I'd greatly appreciate it
You can't get notified straight after your object has been changed, if you dont observe for the "setters" of the object which notify somehow if the object has been changed. Using Observables from android architecture library fixes this issue.
Anyway, you can poll your Object for changes and notify the subscribers if the object has been changed. Using repeatWhen and distintUntilChanged makes sure that you dont get your data emitted if those are not changed. Example (untested) may be:
Observable<MyObject> myObservable = Observable.just(yourObject)
.repeatWhen(o -> o.concatMap(v -> Observable.timer(1000, TimeUnit.MILLISECONDS)))
.distinctUntilChanged();
myObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(d -> Log.d("Data", "Got Object "+ d), e -> Log.e("Error", "Received Error:" + e);
Another better attempt is (like Observables from architecture components) that you modify your Object/Model to observe for changes.
That means that you create a Publish Subject inside your model which gets notified if the setter has been called emitting itself as value. Subscribing to this subject means that all subscribers who subscribe to this subject gets notified as soon as the setter has been called with the object which has been changed.
public static class ModelClass {
private PublishSubject<ModelClass> changeObservable = PublishSubject.create();
private String field1;
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
changeObservable.onNext(this);
}
public Observable<ModelClass> getModelChanges() {
return changeObservable;
}
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ModelClass myModel = new ModelClass ();
myModel.getModelChanges().subscribe(c -> Log.d("Change:" + c));
myModel.setField1("1");
myModel.setField1("2");
myModel.setField1("3");
}
I have a list of transactions. Each transaction has currency and amount information among others. I want to create a list of holdings, so the current amount held by currency. I started with groupBy() and continued with reduce. It seems I have to subscribe before I can do anything with the results, because this gives me an error:
Observable.fromIterable(transactions)
.groupBy(Transaction::getCurrency)
.flatMap(t -> t.reduce(new Holding(t.getKey()), (holding, transaction) -> holding.addTransaction(transaction.getAmount()))
It says "no instance of type variable R exist so that Single conforms to ObservableSource< ? extends R>".
On the other hand if I try this:
Observable.fromIterable(transactions)
.groupBy(Transaction::getCurrency)
.subscribe((GroupedObservable<String, Transaction> r) -> r.reduce(new Holding(r.getKey()), (holding, transaction) -> holding.addTransaction(transaction.getAmount()))
.toObservable()
.subscribe(t -> {
//t is a single Holding.
}
));
I cannot get a list, because I already subscribed to the grouped stream. I could add it up, but I'm pretty sure there is a more elegant solution, but I cannot figure it out.
Solution based on akarnokd's answer:
Observable.fromIterable(transactions)
.groupBy(Transaction::getCurrency)
.flatMapSingle(Observable::toList)
.map(Holding::new)
.toList()
.subscribe(holdings -> {
whatever(holdings);
});
(From my comment to the post):
Try flatMapSingle in the upper case. Also, subscribing from within an onNext handler is a bad practice as you lose the composition properties of RxJava.
This will work for sure
public Single<Map<Integer, List<Category>>> getSubCategoryListById(List<Category> categoryList) {
return Flowable.just(categoryList)
.flatMapIterable(new Function<List<Category>, Iterable<Category>>() {
#Override public Iterable<Category> apply(List<Category> categories) throws Exception {
return categories;
}
})
.filter(new Predicate<Category>() {
#Override public boolean test(Category category) throws Exception {
return category.parent_id != 0;
}
})
.groupBy(new Function<Category, Integer>() {
#Override public Integer apply(Category category) throws Exception {
return category.category_id;
}
})
.flatMapSingle(new Function<GroupedFlowable<Integer, Category>, Single<List<Category>>>() {
#Override public Single<List<Category>> apply(
GroupedFlowable<Integer, Category> integerCategoryGroupedFlowable) throws Exception {
return integerCategoryGroupedFlowable.toList();
}
})
.toMap(new Function<List<Category>, Integer>() {
#Override public Integer apply(List<Category> categories) throws Exception {
return categories.get(0).category_id;
}
});
}
As the documentation says, the reduce function
applies a function to each item emitted by an Observable,
sequentially, and emit the final value.
This is way you get a single value (actually for each Observable of the group you get a single item).
You can defer your reduce operation after you get a list. You could probably replace your first long subscribe with this:
.subscribe(group -> group.toList()
Then you get some Observables based on the number of groups that you have, each emitting a single List of your predefined type.
NOTE: not sure about it, but probably you can replace the first subscribe with a flatMap that transforms your GroupedObservable into an Observable that emit a list of items.
I have to make a call to an API that returns a list of items. For each item of this list, I have to make a call to another API (if the list returns 8 items, I will have to make 8 parallel calls).
I finally have to return a list that I will create with the results of each of these 8 parallel calls.
How can I do that with RxJava ? I think that I have to use a flatMap to transform the result of the first call to a list of Observables, and then I have to use the zip operator to make the parallel calls, but I'm not sure.
Please note that I'm using RxJava2, and without lambdas expressions.
Thanks !
you can do it like this for example,
defer() lets you fetch the data only when subscribing and then creating Observable that emits all items (one by one) in the list of items.
then flatMap() will create Observable that will fetch the data for each item, and you will have now Observable that emit Data objects.
in order to collect it, you can use toList() that will emit single object ( a List) that will contain all the Data fetched by each Observable.
Note, in order to do it in parallel it is important that the fetchDataFromItem() will subscribe on Schedulers.io(), even it the all stream is subscribed on io.
Observable.defer(new Callable<ObservableSource<Item>>() {
#Override
public ObservableSource<Item> call() throws Exception {
List<Item> items = getItems();
return Observable.fromIterable(items);
}
})
.flatMap(new Function<Item, ObservableSource<Data>>() {
#Override
public ObservableSource<Data> apply(#NonNull Item item) throws Exception {
return fetchDataFromItem(item);
}
})
.toList()
.subscribe(new Consumer<List<Data>>() {
#Override
public void accept(#NonNull List<Data> objects) throws Exception {
//do something with the list of all fetched data
}
});
UPDATE:
in case that the items fetching is already Observable, the defer() can be replaced with flatMapIterable() that takes single List of items and transform it to Observable of multiple items:
getItemsObservable()
.flatMapIterable(new Function<List<Item>, Iterable<Item>>() {
#Override
public Iterable<Item> apply(#NonNull List<Item> items) throws Exception {
return items;
}
})
.flatMap(new Function<Item, ObservableSource<Data>>() {
#Override
public ObservableSource<Data> apply(#NonNull Item item) throws Exception {
return fetchDataFromItem(item);
}
})
.toList()
.subscribe(new Consumer<List<Data>>() {
#Override
public void accept(#NonNull List<Data> objects) throws Exception {
//do something with the list of all fetched data
}
});
(Android/Java professional, RxJava/Lambda novice) I'm trying to create the following pojo:
public class ProductCombinedPojo {
private ProductPojo product;
private List<TemplatePojo> templates;
// builder pattern...
}
where ProductPojo is:
public class ProductPojo {
private List<String> templateUrls; // each url returns a TemplatePojo
// lots of other stuff...
}
I have the following Retrofit2 implementations:
#GET
Observable<ProductPojo> getProduct(#Url String url);
#GET
Observable<TemplatePojo> getTemplate(#Url String url);
So the first Observable returns the ProductPojo, the resulting list uf urls within are iterated over and input to the second Observable to get the list of TemplatePojos, finally the results are all combined into a new ProductCombinePojo using a builder pattern. To further complicate matters, due to the nature of the MVP framework, this all has to be done in a single Func0<<Observable<ProductCombinedPojo>> chained RxJava implementation.
I'm having difficulty at the end of the chain cleanly getting the original ProductPojo to inject into the builder. Here is my working but ugly solution (assume mUrl, mProductApi & mTemplateApi are all injected and defined as above):
#Override
public Observable<ProductCombinedPojo> call() {
final ProductPojo[] aProductPojo = new ProductPojo[1]; // <------------ Ugly!
return mProductApi
.getProduct(mUrl)
.flatMapIterable(new Func1<ProductPojo, List<String>>() {
#Override
public List<String> call(ProductPojo productPojo) {
aProductPojo[0] = productPojo; // <------------ Ugly!
return productPojo.getTemplateUrls();
}
})
.flatMap(new Func1<String, Observable<TemplatePojo>>(){
#Override
public Observable<TemplatePojo> call(String templateUrl) {
return mTemplateApi.getTemplate(templateUrl);
}
})
.toList()
.map(new Func1<List<TemplatePojo>, ProductCombinedPojo>() {
#Override
public ProductCombinedPojo call(List<TemplatePojo> templatePojos) {
return ProductCombinedPojo.Builder.aProductCombinedPojo()
.product(aProductPojo[0]) // <------------ Ugly!
.templates(templatePojos)
.build();
}
});
}
How do I rewrite this so that I don't need the ugly final ProductPojo[]? After exhuastive searching and reviewing many similar questions on this forum, I think a
Func2<ProductPojo, List<TemplatePojo>, ProductCombinedPojo>
should be plugged in somewhere but I can't figure out exactly where. Whilst I am interested in what a Lambda solution will look like, the correct answer will be awarded to any solution using the format above.
The problem is that with every operation like map or flatMap, you transform the input into a new output. If you don't include the input in the output, you won't be able to access that input later on.
This is what you're facing here: you want to be able to access the ProductPojo further down the stream.
You could circumvent this by returning a Pair<ProductPojo, List<String>> in your flatMapIterable function, but this doesn't get any better either.
Instead you can create a new Observable in the scope of your ProductPojo:
public Observable<ProductCombinedPojo> call() {
return mProductApi.getProduct(mUrl)
.flatMap(new Func1<ProductPojo, Observable<ProductCombinedPojo>>() {
#Override
public Observable<ProductCombinedPojo> call(ProductPojo productPojo) {
return combinedPojoFor(productPojo);
}
});
}
private Observable<ProductCombinedPojo> combinedPojoFor(final ProductPojo productPojo) {
return Observable.from(productPojo.getTemplateUrls())
.flatMap(new Func1<String, Observable<TemplatePojo>>() {
#Override
public Observable<TemplatePojo> call(String templateUrl) {
return mTemplateApi.getTemplate(templateUrl);
}
})
.toList()
.map(new Func1<List<TemplatePojo>, ProductCombinedPojo>() {
#Override
public ProductCombinedPojo call(List<TemplatePojo> templatePojos) {
return ProductCombinedPojo.Builder.aProductCombinedPojo()
.product(productPojo)
.templates(templatePojos)
.build();
}
});
}
Using lambdas:
public Observable<ProductCombinedPojo> call() {
return mProductApi.getProduct(mUrl)
.flatMap((productPojo) -> combinedPojoFor(productPojo));
}
private Observable<ProductCombinedPojo> combinedPojoFor(final ProductPojo productPojo) {
return Observable.from(productPojo.getTemplateUrls())
.flatMap((templateUrl) -> mTemplateApi.getTemplate(templateUrl))
.toList()
.map((templatePojos ->
ProductCombinedPojo.Builder.aProductCombinedPojo()
.product(productPojo)
.templates(templatePojos)
.build()
));
}
I have a list of Observables like so:
List<Observable<MyObj>> listObservables = new ArrayList<Observable<MyObj>>();
I'd like to combine all Observable in a single one, I can handle it if I know the number of Observable using zip(), for example we have 3 Observable:
Observable<MyObj1> obs1= MyRestClient.getSomeData1();
Observable<MyObj2> obs2= MyRestClient.getSomeData2();
Observable<MyObj3> obs3= MyRestClient.getSomeData3();
I have a wrapper obj:
class MyWrapperObj {
private MyObj1 onj1;
private MyObj2 onj2;
private MyObj3 onj3;
public MyWrapperObj(MyObj1 onj1, MyObj2 onj2, MyObj3 onj3) {
this.onj1 = onj1;
this.onj2 = onj2;
this.onj3 = onj3;
}
}
So I can combine them like so:
Observable<MyWrapperObj> combinedObservable = Observable.zip(obs1, obs2, obs3, new Func3<MyObj1, MyObj2, MyObj3, MyWrapperObj>() {
#Override
public MyWrapperObj call(MyObj1 obj1, MyObj2 obj2, MyObj3 obj3) {
return new MyWrapperObj(obj1, obj2, obj3);
}
});
combinedObservable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<MyWrapperObj>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable throwable) {
}
#Override
public void onNext(MyWrapperObj wrapperObj) {
}
});
Everything is working fine, so my problem is how to organize this combination to be for n Observable?
RESPONSE
as #maciekjanusz mentioned in it's answer I did:
Observable<MyWrapperObj> combinedObservable = Observable.zip(listObservables, new FuncN<MyWrapperObj>() {
#Override
public MyWrapperObjcall(Object... args) {
return null;
}
});
If you want to zip n Observables, put them in a list and apply the public static <R> Observable<R> zip(#NotNull java.lang.Iterable<? extends Observable<?>> ws, rx.functions.FuncN<? extends R> zipFunction) factory method.
List<Observable<String>> observables = Arrays.asList(Observable.just("String 1"), Observable.just("String 2"));
Observable.zip(observables, args -> {
// put your zipping code here
});
For example, if you want to create a list of strings for each emission from all observables:
Observable.zip(observables, Arrays::asList);
Or, if using RxJava on android without retrolambda:
Observable.zip(observables, args -> Arrays.asList(args));
Suppose you have the list:
List<Observable<MyObj>> listObservables
You might consider using Observable.concatDelayError
The advantage if it is finishing all Obbservable's even if any of them finishes with error (resulting in an error in such case).
Remember, that every Observable in this sequence must return the result to onNext method of Subscriber. The result also must have the same type.
Example:
Observable.concatDelayError(listObservables);
You can wait until all observables is complete by using
.zip(observable1, ..., observableN, funcN).first() operators. There is an overload, accepting Observable> argument (as in FlatMap).
First overload takes Iterable> - you can pass list of observables of arbitrary size and second argument - FuncN - receives list of values.