Custom Filter operator RxJava - java

I'm trying to embrace the glory of RxJava and integrating it into my apps. I have written the following code for adding comics whose cumulative cost is not more then the defined budget. To achieve this, I have written 2 implementations.
Uses Observable.create() which is discouraged mostly because of complexities with Subscriptions and backpressure
Uses already available operators in RxAndroid lib.
I would love to get feedback on which implementation is a better one in terms of performance, memory consumption and simplicity if put Subscription and Backpressure handling aside in Observable.create() for a moment?
FIRST IMPLEMENTATION:
Observable<Integer> filterObservable = Observable.create(new ObservableOnSubscribe<Integer>() {
#Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
Timber.d("filterComicsAccordingToBudget():subscribe");
int pageCountOfComicsWithInBudget = 0;
double totalCost = 0.0;
for(MarvelComic comic : getMarvelComicsList()) {
totalCost += Double.valueOf(comic.getPrice());
Timber.d("totalCost: %s budget: %s priceOfComic: %s", totalCost, budget, comic.getPrice());
if(totalCost > budget) {
break;
}
pageCountOfComicsWithInBudget += Integer.valueOf(comic.getPageCount());
Timber.d("pageCount: %s price: %s comicName: %s totalPages: %s", comic.getPageCount(), comic.getPrice(), comic.getTitle(), pageCountOfComicsWithInBudget);
e.onNext(pageCountOfComicsWithInBudget);
}
e.onComplete();
}
});
filterObservable.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Integer>() {
int comicCount = 0;
int pageCountOfComicsWithInBudget = 0;
#Override
public void onSubscribe(Disposable d) {
Timber.d("filterComicsAccordingToBudget():onSubscribe");
}
#Override
public void onNext(Integer pageCountOfComicsWithInBudget) {
Timber.d("filterComicsAccordingToBudget():onNext");
comicCount++;
}
#Override
public void onError(Throwable e) {
Timber.e("onFilterComicsForBudget:onError() %s", e);
}
#Override
public void onComplete() {
Timber.d("filterComicsAccordingToBudget():onComplete");
}
}
});
SECOND IMPLEMENTATION:
Observable.fromIterable(getMarvelComicsList())
.map(new Function<MarvelComic, HashMap<String, Double>>() {
HashMap<String, Double> myMap = new HashMap<String, Double>();
double count = 0;
#Override
public HashMap<String, Double> apply(#NonNull MarvelComic marvelComic) throws Exception {
myMap.put("price", Double.valueOf(marvelComic.getPrice()));
myMap.put("pageCount", Double.valueOf(marvelComic.getPageCount()));
myMap.put("comicsCount", count++);
return myMap;
}
})
.takeWhile(new Predicate<HashMap<String, Double>>() {
double sum;
#Override
public boolean test(#NonNull HashMap<String, Double> map) throws Exception {
Timber.e("sum is: %s", sum);
return (sum += map.get("price")) < 5.00;
}
})
.subscribe(new Observer<HashMap<String, Double>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(HashMap<String, Double> map) {
Timber.e("value in onNext is: %s %s %s", map.get("pageCount"), map.get("price"), map.get("comicsCount"));
}
#Override
public void onError(Throwable e) {
Timber.e("onError()!!! %s",e);
}
#Override
public void onComplete() {
Timber.e("onComplete()!!!");
}
});
I kinda fancy the first implementation since it's more imperative which I'm used to and seems less clunky to me but then I may be completely wrong considering my limited knowledge in RxJava.

I would avoid to create custom Observable for this kind of operations. You can do all you need using the normal RxJava operators.
On fly I would do something like this:
private Observable<Double> getLimitObservable(final double budget) {
return Observable.fromIterable(getMarvelComicsList())
.scan(0D, (aDouble, marvelComic) -> aDouble + marvelComic.getPrice())
.takeWhile(aDouble -> aDouble < budget)
.skip(1);
}
The above code use the scan (also called accumulator) operator for keeping track of the total amount of the prices of the comics. More detail here. So, now a double (representing the total amount) is returned from the new Observable. After this we have the takeWhile that stop the emission of the item until the condition remains true. Eventually I skipped the first item because the abovementioned Observable will emit at least one item (before the condition can be verified).
Observable.zip(getLimitObservable(500d), Observable.fromIterable(getMarvelComicsList()), (aDouble, marvelComic) -> marvelComic)
.subscribe(marvelComic -> Log.d("test", "comic: " + marvelComic.getName()));
Now I combine the previous observable with a new one (using the zip operator) that will generate a new item for each couple of items (one from the first observable, and one from the second) and in this way you will get a number of items equal to the minimum number of items emitted from the two observables. More details here
This will print the list of the first comics in the list, until you reach the budget limit.
I bet there are better solutions, but this is just an example.

Related

How I would use SwitchMap (RXJAVA) in my code?

I'm new to Android development and am currently trying to make a simple MVC app that works with Rest API.
API calls are made without using Retrofit, although this is not so important. The main catch is that using Observable with debounce and SwitchMap I still get too many API calls (and the extra ones should be discarded). The function is called when text is entered (EditText Listener with TextWatcher). And when administered continuously without delay word, every symbol processed by the server and should only be administered when not within 600 milliseconds. Please help me.
public Observable<String> getObservable(final String s){
return Observable
.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext(model.translateText(s));
}
});
}
public Observer<String> observer = new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String s) {
mainView.hideProgress();
mainView.showResult(s);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
};
public void onEditTextChange(String textForTranslate){
mainView.showProgress();
getObservable(textForTranslate)
.debounce(600,TimeUnit.MILLISECONDS)
.switchMap(new Function<String, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(String s) throws Exception {
return Observable.just(s);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
You are creating a new observable every time a character is typed. So multiple observers are created with each having separate debounce (time throttle) and switch but they are not reused. Instead you create a new observable whenever text changes and start rx chain on it.
You need to create a single PublishSubject
private final PublishSubject<String> querySubject = PublishSubject.create();
that emits entered text/query whenever text is changed. Use it in your callback:
public void onEditTextChange(String textForTranslate) {
querySubject.onNext(textForTranslate);
}
And in your main function, subscribe to observable:
querySubject
.debounce(600, TimeUnit.MILLISECONDS)
.distinctUntilChanged()
.switchMap(new Function<String, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(String s) throws Exception {
// perform api call or any other operation here
return Observable.just(s);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
Debounce operator emits single item only after given time (600 ms) has passed. It ignores items if current item is being processed and given time has not passed.
distinctUntilChanged helps in reducing processing of same query.

How to emit whole list only once even if range() operator iterate itself corresponding to range passed in it

Problem
I am having a list which is coming from the backend after calling some API, I need to convert that list into some other type of list. Afterward, I need to add n number of an empty object into the final list. (n number is coming from some logic which suits my application.)
To solve this problem I have written the following code :
Observable.fromIterable(snipList).map(new Function<UserSnipModel, TaskContent>() {
#Override
public TaskContent apply(UserSnipModel userSnipModel) throws Exception {
String displayName = userSnipModel.getDisplayName();
TaskContent.Fields fields = new TaskContent.Fields(userSnipModel.getId(),
displayName, userSnipModel.getUrlCall(), userSnipModel.getRemarks(), userSnipModel.isEnableCall());
Drawable icon = DrawableUtils.getDrawable(HomeActivity.this, userSnipModel.getIconName(), R.drawable.ic_diamond);
return new TaskContent(fields, icon);
}
}).toList().toObservable().flatMap(new Function<List<TaskContent>, ObservableSource<List<TaskContent>>>() {
#Override
public ObservableSource<List<TaskContent>> apply(List<TaskContent> taskContents) throws Exception {
return Observable.range(0, totalNumberOfMembers - numberOfItems).map(new Function<Integer, List<TaskContent>>() {
#Override
public List<TaskContent> apply(Integer integer) throws Exception {
taskContents.add(new TaskContent(new TaskContent.Fields("",
"", "", "", false), null));
return taskContents;
}
});
}
})
.observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()).safeSubscribe(new Observer<ListTaskContent>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(List<TaskContent> taskContents) {
//This method is called multiple time.
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
}
});
When I run this, onNext() is called multiple time which is not working as expected. To handle this problem I added toList() inside the flatMap() then I got List<List<TastContent>> which is also not expected as I need only List<TaskContent>
What can I do to handle this problem?
Thanks in advance.
I'd suggest this solution:
Observable.fromIterable(list)
.map(item -> item.concat("new")) //Converting the items in the initial list
.toList()
.flatMap(data -> Single.zip(
Single.just(data), //Mapped data set
Observable.range(0, data.size()) //Empty list generator
.map(rangeVal -> rangeVal.toString())
.toList(),
(initialList, emptyList) -> {
initialList.addAll(emptyList); //Appending lists
return initialList;
}
)
)
.subscribe(data -> {}, error -> {});

RxJava2 flatMap create duplicate events

I'm relatively new on RxJava2 and I'm getting some weird behaviors, so it's likely that I'm using the tool on the wrong way.
It's a fairly big project, but I've separated the snippet below as a minimum reproducible code:
Observable
.interval(333, TimeUnit.MILLISECONDS)
.flatMap(new Function<Long, ObservableSource<Integer>>() {
private Subject<Integer> s = PublishSubject.create();
private int val = 0;
#Override public ObservableSource<Integer> apply(Long aLong) throws Exception {
val++;
s.onNext(val);
return s;
}
})
.subscribe(new Consumer<Integer>() {
#Override public void accept(Integer integer) throws Exception {
Log.w("value: %s", integer);
}
});
This code simulates events from my rx-stream using an .interval and a flatMap receive those events "do some processing" and uses a Subject to push results down the stream.
The stream is an ongoing process which will have several several events.
This minimum code is silly because I'm pushing only on the apply callback, but in the real case there're several possible moments that a push can happen and the number of events being received during apply is not the same amount that will be sent via the Subject.
What I expected to see with this code is:
value: 2 // 1 got skipped because onNext is called before there's a subscriber.
value: 3
value: 4
value: 5
value: 6 ... etc
what I actually got is:
value: 2
value: 3
value: 3 // 3 twice
value: 4
value: 4
value: 4 // 4 repeated 3 times
value: 5
value: 5
value: 5
value: 5 // 5 repeated 4 times
value: 6
value: 6
value: 6
value: 6
value: 6 // 6 repeated 5 times
... etc
I've also tried to have an Observable<Integer> o = s.share(); and returning it, or return directly s.share(); with the same results.
I kind of understand why this is happening. The ObservableSource gets subscribed again n again n again so there're more events on every loop.
The question:
How can I achieve my expected behavior?
(in case my expected behavior was not clear, please ask more on the comments)
Your PublishSubject is subscribed to multiple times, once per item from interval().
Edit: You will need to pass in a new PublishSubject each time (switch to BehaviorSubject if you'd like to retain the first/last emission); pass that to the long-running process, and ensure that its onComplete is called properly when the long-running process finishes.
Edit
After recent comments I could come up with this kind of a solution:
class MyBluetoothClient {
private PublishSubject<BTLEEvent> statusPublishSubject = PublishSubject.create()
public Observable<BTLEEvent> getEventObservable() {
return statusPublishSubject
}
private void publishEvent(BTLEEvent event) {
statusPublishSubject.onNext(event)
}
public void doStuff1() {
// do something that returns:
publishEvent(BTLEEvent.someEvent1)
}
public void doStuff2() {
// do something else that eventually yields
publishEvent(BTLEEvent.someEvent2)
}
}
And you use it in this way:
MyBluetoothClient client = MyBluetoothClient()
client
.getEventObservable()
.subscribe( /* */ )
///
client.doStuff1()
///
client.doStuff2
Original answer
Will this do?
Observable
.interval(333, TimeUnit.MILLISECONDS)
.flatMap(new Function<Long, ObservableSource<Integer>>() {
private int val = 0;
#Override public ObservableSource<Integer> apply(Long aLong) throws Exception {
val++;
return Observable.just(val);
}
})
.subscribe(new Consumer<Integer>() {
#Override public void accept(Integer integer) throws Exception {
Log.w("value: %s", integer);
}
});
So here is the answer I came up with. I'll mark #Tassos answer as correct as he pointed me out on the right path.
First I need a CachedSubject (a subject that caches items while there's no observers and dispatches them as soon as an observer connects), this is necessary to make sure emissions from inside the apply really gets through. The class mostly wraps a PublishSubject.
class CachedSubject<T> extends Subject<T> {
private PublishSubject<T> publishSubject = PublishSubject.create();
private Queue<T> cache = new ConcurrentLinkedQueue<>();
#Override public boolean hasObservers() {
return publishSubject.hasObservers();
}
#Override public boolean hasThrowable() {
return publishSubject.hasThrowable();
}
#Override public boolean hasComplete() {
return publishSubject.hasComplete();
}
#Override public Throwable getThrowable() {
return publishSubject.getThrowable();
}
#Override protected void subscribeActual(Observer<? super T> observer) {
while (cache.size() > 0) {
observer.onNext(cache.remove());
}
publishSubject.subscribeActual(observer);
}
#Override public void onSubscribe(Disposable d) {
publishSubject.onSubscribe(d);
}
#Override public void onNext(T t) {
if (hasObservers()) {
publishSubject.onNext(t);
} else {
cache.add(t);
}
}
#Override public void onError(Throwable e) {
publishSubject.onError(e);
}
#Override public void onComplete() {
publishSubject.onComplete();
}
}
then I use this class with a switchMap:
Observable
.interval(1000, TimeUnit.MILLISECONDS)
.switchMap(new Function<Long, ObservableSource<Integer>>() {
private Subject<Integer> s = new CachedSubject<>();
private int val = 0;
#Override public ObservableSource<Integer> apply(Long aLong) throws Exception {
val++;
s.onNext(val);
return s;
}
})
.subscribe(new Consumer<Integer>() {
#Override public void accept(Integer integer) throws Exception {
Log.w("value: %s", integer);
}
});
This effectively allows me to receive any number of events on the apply<T t> method and have only 1 Consumer subscribed to it, receiving all the events from it.

rxJava: composing single with completable and return single

my steps are:
create a single value x - may be a little cpu intensive
use value x to perform IO operation. this already returns Completable
return x
so i wanted to do sth like this:
Single<Integer> result =
Single.fromCallable(() -> generate_x)
.COMPOSE_WITH_COMPLETABLE(x -> method_that_returns_completable(x))
.map(x -> x + 1) // i still need x here
how to achieve it?
single.flatMap(new Func1<Integer, Single<? extends Integer>>() {
#Override
public Single<? extends Integer> call(final Integer integer) {
return completable.toSingle(new Func0<Integer>() {
#Override
public Integer call() {
return integer;
}
});
}
})

Making N sequential api calls using RxJava and Retrofit

I have a list of files that I'd like to upload to the backend from an Android device. Due to memory constraints, I'd like to make the second API call only after the first finished, the third after the second finished, and so on.
I wrote something like
private Observable<Integer> uploadFiles(List<File> files) {
return Observable.create(subscriber -> {
for (int i = 0, size = files.size(); i < size; i++) {
UploadModel uploadModel = new UploadModel(files.get(0));
int uploadResult = retrofitApi.uploadSynchronously(uploadModel);
subscriber.onNext(uploadResult);
}
subscriber.onCompleted();
}).subscribeOn(Schedulers.newThread());
}
But I feel like this might be going against the spirit of Rx, and the saying is if you're using Observable.create, you're probably doing it wrong...
Is this a reasonable approach? Is there a better way to achieve this with Retrofit's RxJava integration?
Naively, I would do that (it does not work, though, see below):
return Observable.from(files).concatMap(file -> retrofitApi.upload(uploadModel));
Now the issue is that there is no way to tell retrofit to use only one thread for those calls.
reduce, however, passes the result of one function call to the next, along with the next emitted value from the original observable. That would work, but the function passed to reduce needs to be synchronous. Not good.
Another approach would be to modify the observable recursively:
void getNextFile(int i) {
return retrofit.upload(i).
onNext(result -> getNextFile(i + 1));
}
roughly. But I am not sure how to clean it to make it more readable.
The cleanest I would think would be something like:
Observable.from(files).map(file -> retrofitApi.uploadSynchronously(new UploadModel(file)));
The natives of RxJava would emit all items in Observable.from(...) as if in parallel. That's the best way to think of it as parallel emission. However some cases require real consequent execution of the whole chain. I've come to the following solution, probably not the best one but working.
import rx.Observable;
import rx.Subscriber;
import java.util.Iterator;
import java.util.function.Function;
public class Rx {
public static void ignore(Object arg) {
}
public static <E, R> Observable<Void> sequential(Iterator<E> iterator, Function<E, Observable<R>> action) {
return Observable.create(collectorSubscriber ->
Observable.<Void>create(producerSubscriber ->
producerSubscriber.setProducer(ignoredCount -> {
if (!iterator.hasNext()) {
producerSubscriber.onCompleted();
return;
}
E model = iterator.next();
action.apply(model)
.subscribe(
Rx::ignore,
producerSubscriber::onError,
() -> producerSubscriber.onNext(null));
}))
.subscribe(new Subscriber<Void>() {
#Override
public void onStart() {
request(1);
}
#Override
public void onCompleted() {
collectorSubscriber.onNext(null);
collectorSubscriber.onCompleted();
}
#Override
public void onError(Throwable e) {
collectorSubscriber.onError(e);
}
#Override
public void onNext(Void aVoid) {
request(1);
}
}));
}
}
Example usage would be:
Iterator<? extends Model> iterator = models.iterator();
Rx.sequential(iterator, model -> someFunctionReturnsObservable(model))
.subscribe(...);
This method guarantees chained executions of
Observable<Dummy> someFunctionReturnsObservable(Model model)
Currently the prefered way of creating observables is with fromAsync:
Observable.fromAsync(new Action1<AsyncEmitter<Object>>()
{
#Override
public void call(final AsyncEmitter<Object> emitter)
{
emitter.onNext(object);
emitter.onCompleted();
emitter.setCancellation(new AsyncEmitter.Cancellable()
{
#Override
public void cancel() throws Exception
{
// on unSubscribe() callback
}
});
}
}, AsyncEmitter.BackpressureMode.BUFFER);

Categories

Resources