RxJava Load items on demand - java

I want to load the next item(s) when some other observable emits a new item (a trigger).
This is the code that emits the items:
public Observable<Item> get() {
return idApi.get().flatMap(new Function<List<String>, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(#NonNull List<String> ids) throws Exception {
return Observable.fromIterable(ids);
}
}).flatMap(new Function<String, ObservableSource<Item>>() {
#Override
public ObservableSource<Item> apply(#NonNull final String id) throws Exception {
return dataApi.get(id).map(new Function<Data, Item>() {
#Override
public Item apply(#NonNull Data data) throws Exception {
return new Item(data , id);
});
}
});
}
Trigger Observable:
RxView.clicks(view.findViewById(R.id.button_more)).debounce(500, TimeUnit.MILLISECONDS);
The only way I could get around this was using a Subject and holding a reference to a list of ids which wasn't elegant and didn't seem reactive.
Edit: This is my solution so far, but I had to subscribe to trigger event directly. I don't consider it elegant.
#Override
public Observable<Item> get(final Observable<Object> trigger) {
final PublishSubject<Item> subject = PublishSubject.create();
return idApi.get().flatMap(new Function<List<String>, ObservableSource<Queue<String>>>() {
#Override
public ObservableSource<Queue<String>> apply(#NonNull List<String> ids) throws Exception {
final Queue<String> q = new LinkedList<>(ids);
return Observable.just(q);
}
}).flatMap(new Function<Queue<String>, ObservableSource<Item>>() {
#Override
public ObservableSource<Item> apply(#NonNull final Queue<String> ids) throws Exception {
trigger.subscribeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Object>() {
#Override
public void accept(#NonNull Object o) throws Exception {
if (ids.size() > 0) {
final String id = ids.poll();
dataApi.get(id).map(new Function<Data, Item>() {
#Override
public Item apply(#NonNull Data data) throws Exception {
return new Item(data, id) l
}
}).subscribe(new Consumer<Item>() {
#Override
public void accept(#NonNull Item item) throws Exception {
subject.onNext(item);
}
});
} else {
subject.onComplete();
}
}
});
return subject;
}
});
}

Use zip
public Observable<Item> get(View v) {
return idApi.get().flatMap(new Function<List<String>, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(#NonNull List<String> ids) throws Exception {
return Observable.fromIterable(ids);
}
}).zipWith(RxView.clicks(v).debounce(500, TimeUnit.MILLISECONDS), (n, i) -> n))
.flatMap(new Function<String, ObservableSource<Item>>() {
#Override
public ObservableSource<Item> apply(#NonNull final String id) throws Exception {
return dataApi.get(id).map(new Function<Data, Item>() {
#Override
public Item apply(#NonNull Data data) throws Exception {
return new Item(data , id);
});
}
});
}
to get N items with each click
public Observable<Item> getN(View v, int nitems) {
return idApi.get().flatMap(new Function<List<String>, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(#NonNull List<String> ids) throws Exception {
return Observable.fromIterable(ids);
}
}).buffer(nitems).zipWith(RxView.clicks(v).debounce(500, TimeUnit.MILLISECONDS), (n, i) -> n))
.flatMap(new Function<List<String>, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(#NonNull final List<String> ids) throws Exception {
return Observable.from(ids)
}
}
)
.flatMap(new Function<String, ObservableSource<Item>>() {
#Override
public ObservableSource<Item> apply(#NonNull final String id) throws Exception {
return dataApi.get(id).map(new Function<Data, Item>() {
#Override
public Item apply(#NonNull Data data) throws Exception {
return new Item(data , id);
});
}
}
});
}
Edit: You will still have to use subscribeOn to make sure you are on the main thread for RXView.clicks and on the IO thread for any networking.

Not so good but it works:
your get method:
Observable<Item> get(final String id){
return Observable.defer(() -> {
dataApi.get(id).map(new Function<Data, Item>() {
#Override
public Item apply(#NonNull Data data) throws Exception {
return new Item(data , id);
});
})
}
your click:
private List<String> ids = {//your id s}
private int current = 0;
RxView.clicks(view).flatmap(ignore -> get(ids.get(current++)))
.subscribe(//your Observer)
I'd like recommend that answer with zipwith(), seems better than my answer.

Related

RxJava method conversion to RxJava3

this is a method written in RxJava
public Observable<String> method() {
return model.getOffers()
.filter(new Func1<Offers, Boolean>() {
#Override
public Boolean call(Offers offers) {
if (offers == null)
return false;
return offers.hasSuperOffer();
}
})
.flatMap(new Func1<Offers, Observable<Long>>() {
#Override
public Observable<Long> call(Offers offers) {
Long offerEndTime = offers.getRemainingTime();
if (offerEndTime == null) {
return Observable.empty();
}
AtomicLong remainingTimeSec;
Long currentTimeSec = System.currentTimeMillis() / 1000;
if (remainingTimeSec.get() == -1 && (offerEndTime > currentTimeSec)) {
remainingTimeSec.set(offerEndTime - currentTimeSec);
} else {
return Observable.empty();
}
return Observable.interval(1, TimeUnit.SECONDS)
.onBackpressureDrop()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.take(remainingTimeSec.intValue())
.doOnUnsubscribe(new Action0() {
#Override
public void call() {
}
})
.doOnCompleted(new Action0() {
#Override
public void call() {
}
})
.map(new Func1<Long, Long>() {
#Override
public Long call(Long elapsedTimeSec) {
return remainingTimeSec.getAndDecrement();
}
});
}
})
.map(new Func1<Long, String>() {
#Override
public String call(Long remainingTime) {
return DateUtils.getRemainingTimeStr(remainingTime);
}
});
}
I am trying to convert it to RxJava3 but some parameters have changed:
Func1 has been changed to Function
Action0 has been changed to Action
After I'm making the changes the following error appears at filter:
filter (io.reactivex.rxjava3.functions#io.reactivex.rxjava3.annotations.NonNull Predicate <? MyClass> in Observable cannot be applied to (anonymous.io.reactivex.rxjava3.functions.Function <MyClass.model.Offers.java.lang.Boolean>)
Can anyone help me?
Thank you!

Java andorid RXJava2 post in service to Activity and have to times this same onNext

In service I post my event :
RxBus.getSubject().onNext(eventAddNoteAndRealize) ;
This is my RxBus :
public final class RxBus {
private static final BehaviorSubject<Object> behaviorSubject
= BehaviorSubject.create();
public synchronized static BehaviorSubject<Object> getSubject() {
return behaviorSubject;
}
}
And In my Activity I have this :
DisposableObserver<Object> disposable = RxBus.getSubject().
subscribeWith(new DisposableObserver<Object>() {
#Override
public void onNext(Object o) {
if (o instanceof EventAddNoteAndRealize) {
Toast.makeText(NewMainActivity.this , "next", Toast.LENGTH_LONG).show();
EventAddNoteAndRealize event = new EventAddNoteAndRealize(((EventAddNoteAndRealize) o).getNoteAndRealizeDAOList());
eventAddNoteAndRealize = event;
getRealizeAndNote((EventAddNoteAndRealize)o);
}
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
And a method onNext execute two times this same objects. I do not have idea what I did wrong
Are you sure that you are not emitting the same object 2 times because as i test your code it works good
#Override
public void run(String... args) throws Exception {
DisposableObserver<Object> disposable = RxBus.getSubject().
subscribeWith(new DisposableObserver<Object>() {
#Override
public void onNext(Object o) {
System.out.println(o);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
Observable.interval(100, TimeUnit.MILLISECONDS)
.map(aLong -> {
RxBus.getSubject().onNext(aLong);
return aLong;
}).subscribe();
}
}
final class RxBus {
private static final BehaviorSubject<Object> behaviorSubject
= BehaviorSubject.create();
public synchronized static BehaviorSubject<Object> getSubject() {
return behaviorSubject;
}
make sure that you are not emitting object to times my response is
0
1
2
3
4
5
6
.
.
.

Rx Java 2: How to wrap a callback?

I have this code to wrap a callback in Rx Java 1 and it compiles fine , but now that I have switched to RX Java 2 it does not compile...what is the equivalent in Rx Java 2?
return Observable.fromEmitter(new Action1<AsyncEmitter<Integer>>() {
#Override
public void call(AsyncEmitter<Integer> emitter) {
transObs.setTransferListener(new TransferListener() {
#Override
public void onStateChanged(int id, TransferState state) {
if (state == TransferState.COMPLETED)
emitter.onCompleted();
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
}
#Override
public void onError(int id, Exception ex) {
emitter.onError(ex);
}
});
emitter.setCancellation(new AsyncEmitter.Cancellable() {
#Override
public void cancel() throws Exception {
transObs.cleanTransferListener();
}
});
}
}, AsyncEmitter.BackpressureMode.BUFFER);
UPDATE:
I came up with this, but Do you have to deal with backpressure since its an oncreate call?
return Observable.create(new ObservableOnSubscribe<List<DigitsUser>>() {
#Override
public void subscribe(final ObservableEmitter<List<DigitsUser>> emitter) throws Exception {
mDigitFriends.findFriends((gotEm, users) -> {
emitter.onNext(users);
});
emitter.setCancellable(() -> {
emitter.onNext(null);
});
}
});
If you're worried about backpressure you should use the Flowable class. Here's a quote from the RxJava2 Wiki:
Practically, the 1.x fromEmitter (formerly fromAsync) has been renamed
to Flowable.create.
Here is your example using the Flowable class:
return Flowable.create(new FlowableEmitter<List<DigitsUser>>() {
#Override
public void subscribe(final FlowableEmitter<List<DigitsUser>> emitter) throws Exception {
mDigitFriends.findFriends((gotEm, users) -> {
emitter.onNext(users);
});
emitter.setCancellable(() -> {
emitter.onNext(null);
});
}
}, BackpressureStrategy.BUFFER);

Group a list of events into a dictionary of events by date

I've been trying to learn RxJava2 and I've been struggling with this one..
So, I have a structure that represents an events that goes something like the following:
class Event{
public Date when;
public String eventName;
}
And somewhere I query a list of events from the repository that I want to group by date.
So, given a list of events like:
Event1 at June
Event2 at June
Event3 at July
Event4 at August
Event5 at August
I want to group them so that
June
Event1
Event2
July
Event3
August
Event4
Event5
What I have so far is, in my opinion, very ugly and I am pretty sure I am over-"engineering" this...
repository.getAllEvents()
.toObservable()
.flatMap(new Function<Events, Observable<Event>>() {
#Override
public Observable<Event> apply(#NonNull Events events) throws Exception {
return Observable.fromIterable(events.getEvents());
}
})
.groupBy(new Function<Event, Date>() {
#Override
public Date apply(#NonNull Event event) throws Exception {
return event.when;
}
})
.flatMap(new Function<GroupedObservable<Date, Event>, Observable<Object>>() {
#Override
public Observable<Object> apply(#NonNull GroupedObservable<Date, Event> dateEventGroupedObservable) throws Exception {
final Date key = dateEventGroupedObservable.getKey();
return dateEventGroupedObservable.toList().toObservable().flatMap(new Function<List<Event>, ObservableSource<?>>() {
#Override
public ObservableSource<?> apply(#NonNull List<Event> events) throws Exception {
return Observable.just(new Pair<Date, List<Event>>(key, events));
}
});
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new Observer<Object>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Object o) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
So far, this gives me an observable that delivers a Pair> but as you can see it gets converted to Object and I honestly can't make sense out of the generics hell -.-'
Any tips on how I could approach this?
Thanks
You can achieve this simply by using collect operator:
repository.getAllEvents()
.flatMapIterable(events -> events.getEvents())
.collect(() -> new HashMap<Date, List<Event>>(),
(map, event) -> putEventIntoMap(map, event)
)
...
Without lambdas:
// I assume that getAllEvents returns Events class
repository.getAllEvents()
.flatMapIterable(new Function<Events, Iterable<? extends Event>>() {
#Override
public Iterable<? extends Event> apply(#NonNull Events events) throws Exception {
return events.getEvents();
}
})
.collect(new Callable<HashMap<Date, List<Event>>>() {
#Override
public HashMap<Date, List<Event>> call() throws Exception {
return new HashMap<Date, List<Event>>();
}}, new BiConsumer<HashMap<Date, List<Event>>, Event>() {
#Override
public void accept(#NonNull HashMap<Date, List<Event>> map, #NonNull Event event) throws Exception {
putEventIntoMap(map, event);
}}
)
...
Method to put event into map:
private void putEventIntoMap(HashMap<Date, List<Event>> map, Event event) {
if (map.containsKey(event.when)) {
map.get(event.when).add(event);
} else {
List<Event> list = new ArrayList<>();
list.add(event);
map.put(event.when, list);
}
}
Based on #Maxim Ostrovidov answer I was able to adapt it into the following working solution:
repository.getAllEvents()
// Convert the Single<Events> into an Observable<Events>
.toObservable()
// Transform the stream Events into a List<Event> stream / observable
.flatMapIterable(new Function<Events, List<Event>>() {
#Override
public List<Event> apply(#NonNull Events events) throws Exception {
return events.getEvents();
}
})
// Group each Event from the List<Event> by when (date)
.groupBy(new Function<Event, Date>() {
#Override
public Date apply(#NonNull Event event) throws Exception {
return event.when;
}
})
// For each grouped stream (not sure if its correct to call it like this)
// Lets generate a new stream that is a Pair<Date, List<Event>>
.flatMap(new Function<GroupedObservable<Date, Event>, Observable<Pair<Date, List<Event>>>>() {
#Override
public Observable<Pair<Date, List<Event>>> apply(#NonNull GroupedObservable<Date, Event> dateEventGroupedObservable) throws Exception {
final Date key = dateEventGroupedObservable.getKey();
// toList() takes a big role here since it is forcing
// for the dateEventGroupedObservable to complete and only then
// streaming a Single<List<Event>> which is why I convert it back to observable
return dateEventGroupedObservable.toList().toObservable().flatMap(new Function<List<Event>, Observable<Pair<Date, List<Event>>>>() {
#Override
public Observable<Pair<Date, List<Event>>> apply(#NonNull List<Event> events) throws Exception {
return Observable.just(new Pair<Date, List<Event>>(key, events));
}
});
}
})
// We can now collect all streamed pairs of (Date, List<Event>)
// into an HashMap
.collect(new Callable<HashMap<Date, List<Event>>>() {
#Override
public HashMap<Date, List<Event>> call() throws Exception {
return new HashMap<Date, List<Event>>();
}
}, new BiConsumer<HashMap<Date, List<Event>>, Pair<Date, List<Event>>>() {
#Override
public void accept(#NonNull HashMap<Date, List<Event>> dateListHashMap, #NonNull Pair<Date, List<Event>> dateListPair) throws Exception {
dateListHashMap.put(dateListPair.first, new ArrayList<Event>(dateListPair.second));
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new SingleObserver<HashMap<Date, List<Event>>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onSuccess(HashMap<Date, List<Event>> dateListHashMap) {
}
#Override
public void onError(Throwable e) {
}
});
Yes, it is long and it is ugly but I am pretty sure that with lambdas this would look better. Now, the thing is... This code is used to feed a recycler view adapter so I wonder if it wouldn't be easier to simple do this in an imperative way... oh well, serves the study purpose :)

Get error and resubscribe

Is it possible to resubscribe an Observable and get the error?
The Observable<T> retry() method resubscribes the observable but it consumes the error.
final PublishSubject<Integer> observable = PublishSubject.create();
observable
.flatMap(new Func1<Integer, Observable<Integer>>() {
#Override
public Observable<Integer> call(final Integer integer) {
if (integer % 2 == 0) {
return Observable.just(integer);
} else {
return Observable.error(new Exception("int: " + integer));
}
}
})
.retry()
.subscribe(new Action1<Integer>() {
#Override
public void call(final Integer integer) {
Timber.i("integer: %d", integer);
}
},
new Action1<Throwable>() {
#Override
public void call(final Throwable throwable) {
Timber.e(throwable, "throwable");
}
},
new Action0() {
#Override
public void call() {
Timber.w("onCompleted");
}
});
Observable
.range(0, 10)
.delay(2, TimeUnit.MILLISECONDS)
.subscribe(new Action1<Integer>() {
#Override
public void call(final Integer integer) {
observable.onNext(integer);
}
},
new Action1<Throwable>() {
#Override
public void call(final Throwable throwable) {
observable.onError(throwable);
}
},
new Action0() {
#Override
public void call() {
observable.onCompleted();
}
});
The onError part of observable is never called because .retry() consumes the error.
What you're looking for is retryWhen(). This allows you to pass a Func1 which provides you with the Throwable, that means you can place your onError logic there instead.
This is a good article.

Categories

Resources