I'm studying RxJava to see if I can use it to replace the deprecated AsynTasks in an application created several years ago.
my use case is as follows:
make an http request on Schedulers.io that returns some rows
process the rows separately, in parallel threads
update the UI on main thread only when all rows have been processed
is there a way to do step 2 easily in rx java?
Below is a code example.
Thanks
Observable.fromCallable(()-> {
// 1- get rows form server
ArrayList<HashMap<String, Object>> rows = new ArrayList<HashMap<String, Object>>();
// 2- process rows
for (HashMap row : rows) {
//manipulate row
row.put("test", "test"); <-- code that I want to parallelize
}
return rows;
})
.subscribeOn(Schedulers.io())// Execute in IO thread, i.e. background thread.
.observeOn(AndroidSchedulers.mainThread())// report or post the result to main thread.
.subscribeWith(new Observer<ArrayList<HashMap<String, Object>>>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
}
#Override
public void onNext(#NonNull ArrayList<HashMap<String, Object>> hashMaps) {
}
#Override
public void onError(#NonNull Throwable e) {
}
#Override
public void onComplete() {
//3- update UI....
}
});
After several attempts I came to this solution.
By adding logs to the processRow function I saw that it is called in parallel for multiple rows, as always, at the end the onComplete is called.
Observable.fromCallable(() -> getListResponse()) // 1- get rows form server
.subscribeOn(Schedulers.io())
.flatMapIterable(rowItem -> rowItem)
.flatMap(val -> Observable.just(val) //paralelize
.subscribeOn(Schedulers.computation())
.map(i -> processRow(i) )) // 2- process rows in parallel threads
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Object>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
}
#Override
public void onNext(#NonNull Object listResponse) { }
#Override
public void onError(#NonNull Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
//3- update UI....
}
});
Related
I have to download and parse data from multiple API requests, I am using zip to join all requests together, I am able to get response of each API by iterating Object[] in onNext, but if there's any error in any single API then whole operation will be stopped, i know there's function onErrorResumeNext but what would i pass there in case of error?
suppose, I have 10 API calls in which number 7 api returns 404, then instead of breaking operation I want it to continue calling number 8 api.
Observable<Object[]> combined = Observable.zip(requests, new FuncN<Object[]>() {
#Override
public Object[] call(Object... args) {
return args;
}
}) ;
combined.subscribe(new Subscriber<Object[]>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Object[] apiResponses) {}
});
I have a list of downstream api calls(about 10) that I need to call at once asynchronously. I was using callables till now where I was using
List<RequestContextPreservingCallable <FutureResponse>> callables
I would add the api calls to this list and submit it at the end using executeAsyncNoReturnRequestContextPreservingCallables.
Using Rx java Observables how do I do this?
List<RequestContextPreservingCallable<FutureResponse>> callables = new
ArrayList<RequestContextPreservingCallable<FutureResponse>>();
callables.add(apiOneConnector.CallToApiOne(name));
callables.add(apiTwoConnector.CallToApiTWO(sessionId));
....
//execute all the calls
executeAsyncNoReturnRequestContextPreservingCallables(callables);
You could make use of the zip operator. The zip operator can take multiple observables and execute them simultaneously, and it will proceed after all the results have arrived.
You could then transform these result into your needed form and pass to the next level.
As per your example. Say you have multiple API calls for getting name and session etc, as shown below
Observable.zip(getNameRequest(), getSessionIdRequest(), new BiFunction<String, String, Object>() {
#Override
public Object apply(String name, String sessionId) throws Exception {
// here you will get all the results once everything is completed. you can then take these
// results and transform into another object and returnm from here. I decided to transform the results into an Object[]
// the retuen type of this apply funtion is generic, so you can choose what to return
return new Object[]{name, sessionId};
}
})
.subscribeOn(Schedulers.io()) // will start this entire chain in an IO thread
.observeOn(AndroidSchedulers.mainThread()) // observeOn will filp the thread to the given one , so that the downstream will be executed in the specified thread. here I'm switching to main at this point onwards
.subscribeWith(new DisposableObserver<Object>() {
#Override
public void onNext(Object finalResult) {
// here you will get the final result with all the api results
}
#Override
public void onError(Throwable e) {
// any error during the entire process will be triggered here
}
#Override
public void onComplete() {
//will be called once the whole chain is completed and terminated
}
});
You could even pass a list of observables to the zip as follows
List<Observable<String>> requests = new ArrayList<>();
requests.add(getNameRequest());
requests.add(getSessionIdRequest());
Observable.zip(requests, new Function<Object[], Object[]>() {
#Override
public Object[] apply(Object[] objects) throws Exception {
return new Object[]{objects[0], objects[1]};
}
}).subscribeWith(new DisposableObserver<Object[]>() {
#Override
public void onNext(Object[] objects) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
})
I am new using java RX and I am facing a problem, hopefully someone can give me a clue what Im doing wrong.
The issue:
There are many events I am tracking, such events are triggered like this:
Observable<Long> otherObservable = Observable.empty();
public void myMethod(){
Observable<Long> observable1 = Observable.timer(VARIABLE_TIME, TimeUnit.SECONDS);
final Subscriber<Long> timeSubscriber = new Subscriber<Long>() {
#Override
public void onCompleted() {
// nothing really
}
#Override
public void onError(final Throwable throwable) {
// nothing really
}
#Override
public void onNext(final Long number) {
// Here i do something
}
};
return Observable.merge(timerObservable, otherObservable)
.first()
.subscribe(timeSubscriber);
}
So basically it fires an event after a VARIABLE_TIME.
It works great but now I am facing the fact I have too many events.
So I thought about using debounce and buffer.
What Im trying to do is this:
Still create many observables that emit an event after N seconds.
Collect info from each of them (a long or maybe a String)
After a delay time (buffer time) Send a list with all the collected info to the subscriber.
So far Ive done this:
Observable<List<Long>> otherObservable = Observable.empty();
otherObservable.debounce(10L, SECONDS).buffer(20L, SECONDS);
Observable<List<Long>> observable1 = Observable.timer(VARIABLE_TIME, TimeUnit.SECONDS).buffer(1);
Subscriber<List<Long> > observerSuscriber = new Subscriber<List<Long>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(final Throwable throwable) {
}
#Override
public void onNext(final List<Long> ids ) {
// do something here
}
};
Observable.merge(otherObservable, observable1)
.first()
.subscribe(observerSuscriber);
But like this I still get the message instantly after emitted.
I am wondering if there is anyway to do this? Any ideas? I am using java RX 1.2
After a delay time (buffer time) Send a list with all the collected info to the subscriber.
You answered your own question there! Use the buffer operator:
Flowable<String> stream = ...
Flowable<List<String>> lists = stream.buffer(5, TimeUnit.SECONDS);
I am new in Rx android.When i try to call
Observable.from(imagesMulty).map(image -> printImage(image)).subscribe();
public void printImage(Image image)
{
Subscription addImage;
addImage = retrofit.create(Restapi.class).addImage(image)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Response<SlideResponse>>() {
#Override
public final void onCompleted() {
dismissProgress();
}
#Override
public final void onError(Throwable e) {
e.printStackTrace();
dismissProgress();
}
#Override
public void onNext(Response<SlideResponse> apiResponse) {
dismissProgress();
if (apiResponse.code() == 201) {
}
}
});
subscriptions.add(addSlide);
}
its showing No Instance of type variable R exist so that Observable conforms to Observable.Let me know whats is this error and how to resolve.My requirement is make api call one after other.
i tried to add compile "com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0" also but still error exist.
RxJava2 doesn't allow you to use Observable.from() function. Use are using com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0 with RxJava. (Version confliction)
Best implementation should be like this (in Rxjava2)
Disposable subscription;
subscription = Observable.just(imagesMulty)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
#Override
public void accept(#NonNull String s) throws Exception {
// do retrofit stuff
}
}, new Consumer<Throwable>() {
#Override
public void accept(#NonNull Throwable throwable) throws Exception {
throwable.printStackTrace();
}
});
subscription.dispose(); // Call in onDestroy() or onPause()
If you want to combine or zip your retrofit stuff with map operator, consider using zip, concat or combinelatest ...
you can try this example. This may help to solve your problem
https://github.com/dustin-graham/RxAndroid-Sample
Observable.create(new Observable.OnSubscribe<Integer>() {
#Override
public void call(Subscriber<? super Integer> subscriber) {
subscriber.onStart();
subscriber.onNext(1);
subscriber.onCompleted();
}
}).delaySubscription(5, TimeUnit.SECONDS).subscribe(new Subscriber<Integer>() {
#Override
public void onCompleted() {
Log.e("TAG", String.format("(%s) - onCompleted", System.currentTimeMillis()));
}
#Override
public void onError(Throwable e) {
Log.e("TAG", String.format("(%s) - onError", System.currentTimeMillis()), e);
}
#Override
public void onNext(Integer integer) {
Log.e("TAG", String.format("(%s) - onNext: %s", System.currentTimeMillis(), integer));
}
#Override
public void onStart() {
super.onStart();
Log.e("TAG", String.format("(%s) - onStart", System.currentTimeMillis()));
}
});
output:
(1485004553817) - onStart
(1485004558818) - onNext: 1
(1485004558819) - onCompleted
why onStart event not waiting to delaySubscription and calling soon ?
i want aware when call method called
Documentation says -
onStart -
This method is invoked when the Subscriber and Observable have been connected but the Observable has not yet begun to emit items or send notifications to the Subscriber.
delaySubscription:
Returns an Observable that delays the subscription to the source Observable by a given amount of time.
onNext is invoked only when the subscription is achieved. onStart is called the moment a connection is established. Thus, it works as expected according to the definition.
You can try commenting the code subscriber.onStart(); and execute the same again to notice that onStart is still called at the beginning. The intentional execution did not really invoke the said method because this was executed not on the real subscriber we created, but the one which was a result of delaySubscription (of type OnSubscribeDelaySubscription).
Below is a snippet which can probably help you achieve what you're looking for:
public static void main(String[] args) throws UnsupportedEncodingException, IOException {
Observable.timer(5, TimeUnit.SECONDS).flatMap(val -> {
System.out.println("Initialize");
return Observable.create(subscriber -> {
System.out.println("onsubscribe");
doMyAsyncStuff(subscriber);
});
}).subscribe(val -> System.out.println(val));
Observable.timer(10, TimeUnit.SECONDS).toBlocking().first();
}
We initialize a timer, once timer is executed, we perform some task in flatMap which should be the same as what you earlier did with onStart. Once that task is executed, we emit a Observable which emits all the elements that you could have consumed earlier with onNext calls.