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.
Related
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?
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();
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))
)
}
});
I've seen a lot of examples of how to turn finite things like arrays or Iterables into Observables, but I'm not sure I understand how to make an Observable out of something live and effectively unbounded like an event receiver. I studied the RxJava2 docs and came up with this, using an Android LocationListener as an example.
Is there a simpler and/or more correct way to do this? I'm aware of the "RxBus" concept, but it seems like a way of clinging to the old event bus paradigm.
final Observable<Location> locationObservable = Observable.create(new ObservableOnSubscribe<Location>() {
final LocationManager mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
#Override
public void subscribe(final ObservableEmitter<Location> emitter) throws Exception {
final LocationListener listener = new LocationListener() {
#Override
public void onLocationChanged(final Location location) {
emitter.onNext(location);
}
#Override
public void onStatusChanged(final String s, final int i, final Bundle bundle) {
// TODO ???
}
#Override
public void onProviderEnabled(final String s) {
// TODO ???
}
#Override
public void onProviderDisabled(final String s) {
// TODO ???
}
};
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
emitter.setCancellable(new Cancellable() {
#Override
public void cancel() throws Exception {
mLocationManager.removeUpdates(listener);
}
});
emitter.setDisposable(new Disposable() {
private AtomicBoolean mDisposed;
#Override
public void dispose() {
if(mDisposed.compareAndSet(false, true)) {
mLocationManager.removeUpdates(listener);
}
}
#Override
public boolean isDisposed() {
return mDisposed.get();
}
});
}
});
using Observable.create() is indeed a correct way.
However, with RxJava2 the default way is to extend an Observable, you can see this answer for greater details.
some comments though regarding your implementation:
- there is no point setting both Cancellable and Disposable, as the later one will cancel/dispose the first one, you can see the difference between them here.
- I think it's best practice, to register cancellable/disposable before you start listening to update, in order to prevent weird edge cases races.
after toList operator performs, original Flowable<<\List>> converts to Single<<\List>>. and it turns out if I create Consumer to subscribe to Single,
the Consumer value type cannot be changed except Object?
#Override
public void loadBannerData(final ADFilterType adFilterType) {
remoteListDataSource.getBannerListData(adFilterType)
.flatMap(new Function<List<BannerBeanList.BannerBean>, Publisher<?>>() {
#Override
public Publisher<?> apply(List<BannerBeanList.BannerBean> bannerBeen) throws Exception {
return Flowable.fromIterable(bannerBeen);
}
})
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
/******************************Consume Value Type**************************
.subscribe(new Consumer<List<BannerBeanList.BannerBean>>() {
#Override
public void accept(List<BannerBeanList.BannerBean> bannerBeens) throws Exception {
mainTabView.showMainBanner(bannerBeens);
}
});
*****************************************************************************/
}
From my comment: this happens because you have Publisher<?> instead of Publisher<BannerBeanList.BannerBean> in your code. Often IDEs can't infer types of lambdas or functional interfaces and you'll end up with ? or Object as their type when using some generate/convert refactoring function.