How to preserve informations about original observable on RxJava2 - rx-java2

I have a REST call returning a collection (the original), this collection is filtered but on the subscribe onSuccess I what to obtain both the original list and the filtered one.
I don't know how to 'pass' this second element, which operator should I use to obtain this result?
I show a simplified version of my code below
Observable.fromCallable(new Callable<List<Integer>>() {
#Override public List<Integer> call() throws Exception {
// dynamic list obtained from REST call
// for simplicity here I return a list
return Arrays.asList(1, 2, 3, 4);
}
})
.flatMap(new Function<List<Integer>, ObservableSource<Integer>>() {
#Override public ObservableSource<Integer> apply(List<Integer> integers) throws Exception {
return Observable.fromIterable(integers);
}
})
.filter(new Predicate<Integer>() {
#Override public boolean test(Integer integer) throws Exception {
return integer > 2;
}
})
.toList()
.subscribe(new SingleObserver<List<Integer>>() {
#Override public void onSubscribe(Disposable d) {}
#Override public void onSuccess(List<Integer> value) {
///////////////////
// here I want both original and filtered list
///////////////////
}
#Override public void onError(Throwable e) {}
});

One way is with ConnectableObservable. You need to share the emissions of your initial stream. Something like this
ConnectableObservable<List<Integer>> connectableObservable
= Observable.fromCallable(() -> {
// dynamic list obtained from REST call
// for simplicity here I return a list
return Arrays.asList(1, 2, 3, 4);
}).publish();
Single.zip(connectableObservable.flatMapIterable(integers -> integers)
.filter(integer -> integer > 2)
.toList(),
connectableObservable.elementAtOrError(0),
(integers, lists) -> combine(integers, lists))
.subscribe(o -> {
///////////////////
// here you ll have a new object containing both the initial list and the filtered list
///////////////////
});
connectableObservable.connect();

Related

RxJava2: Using Flowable with zipWith

I'm trying to make the following code work me but something is wrong, here is a snippet:
private void myMethod() {
Flowable.fromIterable(cache)
.zipWith(this::doesExist, (record, exist) -> {
// do stuff
return true;
}).subscrib();
}
private Flowable<Boolean> doesExist(CacheRecord record) {
// Do something
return Flowable.just(true);
}
This doesn't compile, any idea?
UPDATE:
Any thoughts about the following snippet:
Flowable.fromIterable(m_cache) //
.flatMapCompletable(cachedStation -> {
return Single.zip(Single.just(cachedStation), doesIssueExist(cachedStation), (record, exist) -> {
System.out.println(cachedStation + ", " + exist);
return true;
}).toCompletable();
}).subscribe();
Your doesExist method requires a CacheRecord as a parameter. But the method reference you have given this::doesExist sends an instance of Subscriber<? super Object> that's why the incompatible type error is showing.
The expanded form of your method is given below.
private void myMethod() {
Flowable.fromIterable(cache)
.zipWith(new Publisher<Object>() {
#Override
public void subscribe(Subscriber<? super Object> s) {
doesExist(s);
}
}, (record, exist) -> {
// do stuff
return true;
}).subscribe();
}
Here, the first parameter to zipWith
new Publisher<Object>() {
#Override
public void subscribe(Subscriber<? super Object> s) {
doesExist(s);
}
}
is what you have shortened as this::doesExist
As you can see the zipWith requires the first parameter a Publisher, and you have created an anonymous Publisher, and in the subscribe method you are calling doesExist(s) by sending the Subscriber<? super Object> s, which is not the required type. Your method reference statement this::doesExist does exactly the above operation and that's why the incompatible type error is shown by the compiler.
If you are trying to zip the Flowable with the flowable returned by doesExist method, you can directly call it, without method reference, by passing a valid CacheRecord object as follows
Flowable.fromIterable(cache)
.zipWith(doesExist(anotherCache), (record, exist) -> {
// do stuff
return true;
}).subscribe();
Note: See method reference for more information
Update: If you are trying to pass the items emitted by fromIterable to doesExist method and get combined result boolean and cacheRecord, then
create a holder class as follows
class CacheRecordResult {
CacheRecord cacheRecord;
boolean isExist;
public CacheRecordResult(CacheRecord cacheRecord, boolean isExist) {
this.cacheRecord = cacheRecord;
this.isExist = isExist;
}
}
Then subscribe to CacheRecordResult as follows
private void myMethod() {
Flowable.fromIterable(cache)
.flatMap(cacheRecord -> doesExist(cacheRecord)
.map(exist -> new CacheRecordResult(cacheRecord, exist)))
.subscribe(cacheRecordResult -> {
CacheRecord cacheRecord = cacheRecordResult.cacheRecord;
boolean isExist = cacheRecordResult.isExist;
});
}

Android Room with RXJava2; onNext() of emitter is not properly triggered

I am switching from async tasks to rxjava2 and have some issues with my code tests.
I have a room table of elements that have a certain monetary amount. On a usercontrol that is called DisplayCurrentBudget, a sum of all amounts should be displayed. This number must refresh everytime a new element is inserted. I tackled the requirement in two ways, but both produce the same result: My code does not care if the database is updated, it only updates when the fragment is recreated (onCreateView).
My first attempt was this:
//RxJava2 Test
Observable<ItemS> ItemObservable = Observable.create( emitter -> {
try {
List<ItemS> movies = oStandardModel.getItemsVanilla();
for (ItemS movie : movies) {
emitter.onNext(movie);
}
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
});
DisposableObserver<ItemS> disposable = ItemObservable.
subscribeOn(Schedulers.io()).
observeOn(AndroidSchedulers.mainThread()).
subscribeWith(new DisposableObserver<ItemS>() {
public List<ItemS> BadFeelingAboutThis = new ArrayList<ItemS>();
#Override
public void onNext(ItemS movie) {
// Access your Movie object here
BadFeelingAboutThis.add(movie);
}
#Override
public void onError(Throwable e) {
// Show the user that an error has occurred
}
#Override
public void onComplete() {
// Show the user that the operation is complete
oBinding.DisplayCurrentBudget.setText(Manager.GetBigSum(BadFeelingAboutThis).toString());
}
});
I already was uncomfortable with that code. My second attempt produces the exact same result:
Observable<BigDecimal> ItemObservable2 = Observable.create( emitter -> {
try {
BigDecimal mySum = oStandardModel.getWholeBudget();
emitter.onNext(mySum);
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
});
DisposableObserver<BigDecimal> disposable = ItemObservable2.
subscribeOn(Schedulers.io()).
observeOn(AndroidSchedulers.mainThread()).
subscribeWith(new DisposableObserver<BigDecimal>() {
#Override
public void onNext(BigDecimal sum) {
// Access your Movie object here
oBinding.DisplayCurrentBudget.setText(sum.toString());
}
#Override
public void onError(Throwable e) {
// Show the user that an error has occurred
}
#Override
public void onComplete() {
// Show the user that the operation is complete
}
});
Any obvious issues with my code?
Thanks for reading, much appreciate it!
Edit:
I was asked what Manager.GetBigSum does, it actually does not do much. It only adds BigDecimal-Values of an Item list.
public static BigDecimal GetBigSum(List<ItemS> ListP){
List<BigDecimal> bigDList = ListP.stream().map(ItemS::get_dAmount).collect(Collectors.toList());
return bigDList.stream()
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
Further, I simplified the query. But it still does not care about DB updates, only about fragment recreation:
Single.fromCallable(() -> oStandardModel.getItemsVanilla())
.map(Manager::GetBigSum)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
e -> oBinding.DisplayCurrentBudget.setText(e.toString())
);
Your rx logic has no error. That should be internal error in your getWholeBudget.
But why you write rx so complex?
For your case, you can just write:
Single.fromCallable(() -> oStandardModel.getItemsVanilla())
.map(Manager::GetBigSum)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
e -> oBinding.DisplayCurrentBudget.setText(sum.toString()),
e -> log.error(e));
I solved it this way:
oStandardModel.getItemJointCatLive().observe(this, new Observer<List<ItemJointCat>>() {
#Override
public void onChanged(#Nullable final List<ItemJointCat> oItemSP) {
Single.fromCallable(() -> oStandardModel.getWholeBudget())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
e -> oBinding.DisplayCurrentBudget.setText(e.toString())
);
}
});
My mistake was that I assumed RXjava2 does not need an onchanged event...now i just use onchanged event of livedata observer to trigger a simple rxjava2 query.
Do you think there is anything wrong with that approach?

RxJava Single.concat

I have the following code:
model.getCategories()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<Category>>()
{
#Override
public void call(final List<Category> categories)
{
model.getUnits()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<Unit>>()
{
#Override
public void call(List<Unit> units)
{
view.showAddProductDialog(units, categories);
}
});
}
});
I have this ugly nesting. How can I fix it.
I tried something like this:
Single.concat(model.getCategories(), model.getUnits())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<? extends Object>>()
{
#Override
public void call(List<? extends Object> objects)
{
// do something
}
});
But the problem is that I cannot determinate whether it is List<Category> or List<Unit> comes.
Is there a way to use concat and detect what kind of stream comes (List<Category> or List<Unit> or something else) ?
Also I need to detect that all observers are completed to perform another action.
Thanks in advice.
Use Single.zip():
Single.zip(
getCategories().subscribeOn(Schedulers.io()),
getUnits().subscribeOn(Schedulers.io()),
(a, b) -> Pair.of(a, b)
)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
pair -> view.showAddProductDialog(pair.first, pair.second),
error -> showError(error.toString())
)
where Pair can be any tuple implementation, i.e., this.

Convert from `ObservableEmitter` to `Observer` in rx-java2

Is there a easy way to convert from io.reactivex.ObservableEmitter<T>to io.reactivex.Observer<T>? I could not find function to do that in rx-java2 library.
The implementation seems to be trivial:
public static <T> Observer<T> toObserver(ObservableEmitter<T> oe) {
return new Observer<T>() {
#Override
public void onSubscribe(Disposable d) {
oe.setDisposable(d);
}
#Override
public void onNext(T t) {
oe.onNext(t);
}
#Override
public void onError(Throwable e) {
oe.onError(e);
}
#Override
public void onComplete() {
oe.onComplete();
}
};
}
but it feels that it should be part of standard library implementation, as it provides transformation between two core types in rx-java2.
Basically I am trying to migrate following code from rxjava 1 to 2
class X<T, O1, O2> implements Transformer<T, Either<O1, O2>> {
Transformer<T, O1> t1;
Transformer<T, O2> t2;
#Override
public Observable<Either<O1, O2>> call(Observable<T> input) {
return input.flatMap(new Func1<T, Observable<Either<O1, O2>>>() {
#Override
public Observable<Either<O1, O2>> call(final T t) {
return Observable.<Either<O1, O2>>create(new OnSubscribe<Either<O1, O2>>() {
#Override
public void call(final Subscriber<? super Either<O1, O2>> sub) {
t1.call(Observable.just(t)).map(o1 -> Either.<O1, O2>left(o1)).subscribe(sub);
t2.call(Observable.just(t)).map(o2 -> Either.<O1, O2>right(o2)).subscribe(sub);
}
});
}
});
}
}
Notice that OnSubscribe provides Subscriber interface which I can then use to subscribe to two other Observable's, with rxjava 2 conversion is needed.
Looks like you need publish(Function):
(Your code looks convoluted and was violating the protocol in v1 by the way).
ObservableTransformer<T, O1> t1 = ...
ObservableTransformer<T, O2> t2 = ...
ObservableTransformer<T, Either<O1, O2>> combiner = o ->
o.publish(g -> Observable.merge(
t1.apply(g).map(o1 -> Either.<O1, O2>left(o1)),
t2.apply(g).map(o2 -> Either.<O1, O2>right(o2))
));
If you really want to stick to the outer flatMap (in case the inners go async), use merge() instead of create:
return input.flatMap(new Func1<T, Observable<Either<O1, O2>>>() {
#Override
public Observable<Either<O1, O2>> call(final T t) {
Observable<T> just = Observable.just(t);
Observable.merge(
t1.call(just).map(o1 -> Either.<O1, O2>left(o1)),
t2.call(just).map(o2 -> Either.<O1, O2>right(o2))
)
}
});

On Gwt TreeViewModel getNodeInfo() method

I can't understand that part, neither trying the showcase examples.
I'm using an extension of AsyncDataProvider to bind my tree to RPC service. Here's my method:
public <T> NodeInfo<?> getNodeInfo(T value) {
/*
if (value instanceof Categoria) {
dataProvider.setCurrentParent((Categoria)value);
}
*/
return new DefaultNodeInfo<Categoria>(dataProvider, new CategoriaCell());
}
"currentParent" is my stuff: except for (null => root) values, I set the parent to pass via RPC to my service. Actually, in my widget code:
dataProvider = new CategorieTreeDataProvider() {
#Override
protected void onRangeChanged(HasData<Categoria> display) {
updateTree(getCurrentParent());
}
};
private void updateTree(Categoria categoria) {
rpcService.getCategorie(categoria, new AsyncCallback<Categoria[]>() {
#Override
public void onSuccess(Categoria[] result) {
dataProvider.updateRowCount(result.length, true);
dataProvider.updateRowData(0, Arrays.asList(result));
}
#Override
public void onFailure(Throwable caught) {
Window.alert(caught.toString());
}
});
}
My rpc-server code, however, is working as expected:
#Override
public Categoria[] getCategorie(Categoria parent) {
List<Categoria> categoryList = categorieDao.listByProperty("parent", parent);
for (Categoria c : categoryList) {
if (categorieDao.listByProperty("parent", c).size() == 0) {
c.setLeaf(true);
}
}
return categoryList.toArray(new Categoria[0]);
}
**Then I add some data to my Categories: 'GrandFather', 'Father' and 'Son'.
Unfortunately, after loading my widget, I see:
The grandfather correctly, with his "+" how expected;
Then I click it and...
The grandfather disappear and I see 'Father' with his '+'
same for father -> son
I suspect the bug is in updateRowCount / updateRowData usage.**
Any ideas?
The getNodeInfo is called whenever you open a node so you have to create distinct DataProvider for each of the nodes's childs.
public <T> NodeInfo<?> getNodeInfo(T value) {
if (value == null) {
return new DefaultNodeInfo<Category>(dataProvider, new CategoriaCell());
}
else if (value instanceof Categoria) {
Category category = (Category)value;
return new DefaultNodeInfo<Grandfather>(new ListDataProvider<Grandfather>(category.getGrandFathers()),new GrandFatherCell());
}
else if (value instanceof Grandfather) {
Grandfather grandfather = (Grandfather)value;
return new DefaultNodeInfo<Father>(new ListDataProvider<Father>(granfather.getFathers()),new FatherCell());
}
else if (value instanceof Father) {
//same as above but with fathers.
}
}
The category.getGrandFathers() function can for example do a RPC request to the server or just return the list if you retrieve everything in one RPC request.
UPDATE based on comment:
So in case you have only one class and want to achieve a dynamic CellTree (number of levels are not pre-determined) you could take following approach.
public <T> NodeInfo<?> getNodeInfo(T value) {
if (value == null) {
return new DefaultNodeInfo<Category>(dataProvider, new CategoriaCell());
}
else {
Category category = (Category)value;
return new DefaultNodeInfo<Category>(new ListDataProvider<Category>(category.getSubCategories()),new CategoryCell());
}
}
category.getSubCategories() is either an RPC call which retrieves the subcategories for the current category or if the Category class is a linked list type datastructure it could just return the list of subcategories.
Each data provider updates a given "list" (child nodes of a given parent node), so you have to use a distinct data provider instance for each parent node, or your calls will update some random list.