Why this always runs on main thread? - java

I tried to implement a very simple example of RxAndroid2. When I try to run this code, it makes me feel confused.
ArrayList<Integer> arr = new ArrayList<>();
arr.add(0);
arr.add(1);
arr.add(2);
arr.add(3);
arr.add(4);
arr.add(5);
arr.add(6);
Observable.fromIterable(arr)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Integer>() {
#Override
public void onSubscribe(Disposable d) {
LogUtil.logD(TAG, Thread.currentThread().getName());
}
#Override
public void onNext(Integer integer) {
LogUtil.logD(TAG, Thread.currentThread().getName());
}
#Override
public void onError(Throwable e) {
LogUtil.logD(TAG, Thread.currentThread().getName());
}
#Override
public void onComplete() {
LogUtil.logD(TAG, Thread.currentThread().getName());
}
});
And the log always tells me that it runs on main thread only.
Any idea around this? It would be very nice, if you can explain me more about observeOn() and subscribeOn().
EDITED: This code still gives me all main thread.
ArrayList<Integer> arr = new ArrayList<>();
arr.add(0);
arr.add(1);
arr.add(2);
arr.add(3);
arr.add(4);
arr.add(5);
arr.add(6);
Observable.fromIterable(arr)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(new Consumer<Disposable>() {
#Override
public void accept(Disposable disposable) throws Exception {
LogUtil.logD(TAG, Thread.currentThread().getName());
}
})
.doOnNext(new Consumer<Integer>() {
#Override
public void accept(Integer integer) throws Exception {
LogUtil.logD(TAG, Thread.currentThread().getName());
}
})
.subscribe(integer -> {
LogUtil.logD(TAG, Thread.currentThread().getName());
});

You have observeOn(AndroidSchedulers.mainThread())
And the javadoc for observeOn() clearly states:
Modifies an ObservableSource to perform its emissions and notifications on a specified Scheduler, asynchronously with an unbounded buffer with Flowable.bufferSize() "island size".
So why are you surprised that things happen on the main thread?!
And just to be clear about this: your code configures the Observable to use the main thread.
Looking further into the related APIs, I am guessing you have to call start() on the your scheduler, like:
Scheduler newThreadScheduler = Schedulers.newThread();
newThreadScheduler.start();
Observable.fromIterable(arr)
.subscribeOn(newThreadScheduler)
.observeOn(newThreadScheduler)

You can move the operators around:
Observable.fromIterable(arr)
.subscribeOn(Schedulers.newThread())
// <------------------------
.doOnSubscribe(new Consumer<Disposable>() {
#Override
public void accept(Disposable disposable) throws Exception {
LogUtil.logD(TAG, Thread.currentThread().getName());
}
})
.doOnNext(new Consumer<Integer>() {
#Override
public void accept(Integer integer) throws Exception {
LogUtil.logD(TAG, Thread.currentThread().getName());
}
})
.observeOn(AndroidSchedulers.mainThread()) // <--------------------------
.subscribe(integer -> {
LogUtil.logD(TAG, Thread.currentThread().getName());
});
This no longer executes the code in doOnSubscibe and doOnNext on the main thread, only the subscribe handler gets executed on the main thread.

Related

SmallRye Mutiny unable to process events asynchronously using subscription

I am developing an application that returns Multi<String>, I would like to make some modifications to it, so I have added some methods, but for some reason it does not enter the next method at all.
My other methods are working absolutely fine. Because I am able to collect it and add it to a List, but I want to do some execution asynchronously, so using this approach.
private final ManagedExecutor managedExecutor;
public void writeTo(StreamingInfo streamingInfo) {
streamingInfo
.getEvents()
.runSubscriptionOn(managedExecutor)
.subscribe()
.withSubscriber(
new Subscriber < String > () {
#Override
public void onSubscribe(Subscription s) {
System.out.println("OnSubscription Method");
System.out.println("ON SUBS END");
}
#Override
public void onNext(String event) {
System.out.println("On Next Method");
}
#Override
public void onError(Throwable t) {
System.out.println("OnError Method");
}
#Override
public void onComplete() {
System.out.println("On Complete Method");
}
});
}
I get the following output:
OnSubscription Method
ON SUBS END
Which means that your subscription is not working for some reason. If I do not add subscription and directly collect to List then everything works as expected. Can anyone suggest what am I doing wrong here?
This is because the underlying Reactive Streams specification that SmallRye Mutiny implements has a built-in backpressure mechanism. The client (in your case your subscriber) needs to request the next item manually from the producer (events) otherwise, no item is sent down the reactive pipeline.
You need to save the Subscription object you receive in the onSubscribe method and call its request(long) method when you can process next item(s):
.withSubscriber(
new Subscriber<String>() {
private Subscription subscription;
#Override
public void onSubscribe(Subscription s) {
System.out.println("OnSubscription Method");
System.out.println("ON SUBS END");
subscription = s;
subscription.request(1);
}
#Override
public void onNext(String event) {
System.out.println("On Next Method");
subscription.request(1);
}
#Override
public void onError(Throwable t) {
System.out.println("OnError Method");
}
#Override
public void onComplete() {
System.out.println("On Complete Method");
}
});
In SmallRye there is also an easier way to do this:
events
.onSubscription()
.invoke(() -> {
System.out.println("OnSubscription Method");
System.out.println("ON SUBS END");
})
.onItem()
.invoke(event -> System.out.println("On Next Method"))
.onFailure()
.invoke(t -> System.out.println("OnError Method"))
.onCompletion()
.invoke(() -> System.out.println("On Complete Method"))
.subscribe()
.with(value -> {});

Multiple requests using Retrofit and RXJava

I have a service called verifyData. I need call this service N number of times.
Retrofit 2
RXJava
Service
#FormUrlEncoded
#POST("verifyData")
Observable<Adeudos> Adeudos(
#Field("id") int id_user
);
Simple Call
Observable<Adeudos> respuesta = services.verifyData(1);
respuesta.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Adeudos>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Adeudos adeudos) {
}
});
I need execute this "method" usign this array
List<String> ids = new ArrayList(); // 1,2,3,4,5,6,7
SOLUTION
Add retrolambda in my gradle
Java
Observable.from(ids)
.flatMap(s -> services.verifyData(ids).subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Adeudos>() {
#Override
public void onCompleted() {
Log.e("Completed :"," Completed\n");
}
#Override
public void onError(Throwable e) {
Log.e("SplashInteractorImpl : ",e.getMessage()+"\n");
}
#Override
public void onNext(Adeudos adeudos) {
Log.e("SplashInteractorImpl : ",adeudos.getMessage()+"\n");
}
});
If you want to make sequential calls, use .concatMap
Observable.from(ids)
.concatMap(s -> services.verifyData(Integer.parseInt(s)))
//subscription
If you want parallel calls, use a flatMap() with a maxConcurrent parameter
Observable.from(ids)
.flatMap(s -> services.verifyData(Integer.parseInt(s))
.subscribeOn(Schedulers.io()), maxConcurrent)
//subscription
So it creates stream of id from your list and modifies it to make your api calls.

Do something after multiple observables have completed

I'm using RXJava on Android and trying to chain together multiple API calls and do something after both API calls have finished. My API calls all look similar to the code sample provided. Basically make the API call, write each record to the DB in onNext, and after all records have been written, update some cache. I want to fire off both of these calls asynchronously and then after both have hit onCompleted, then do something else. What's the proper way in RX to do this? I don't think I need zip as I don't need to tie together the different streams. I was thinking maybe merge, but my two API calls return a different type of Observable. Please let me know. Thanks.
getUsers()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(Observable::from)
.subscribe(new Subscriber<User>() {
#Override
public void onCompleted() {
updateUserCache();
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "Error loading users", e);
}
#Override
public void onNext(User user) {
insertUserToDB(user);
}
});
getLocations()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(Observable::from)
.subscribe(new Subscriber<Location>() {
#Override
public void onCompleted() {
updateLocationCache();
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "Error loading Locations", e);
}
#Override
public void onNext(Location location) {
insertLocationToDB(location);
}
});
You are thinking correctly. You should use zip operator.
Every one of your function should make a call, write to database and do everything you need. Theat zip output function differently: when it is invoked, you can be sure all Observable's has completed successfully -> just complete your reactive stream.
Create a list of Observable:
List<Observable<?>> observableList = new ArrayList<>();
observableList.add(
getUsers()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(Observable::from)
.insertUserToDB(user)
.toList());
observableList.add(
getLocations()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(Observable::from)
.insertLocationToDB(location)
.toList());
Then zip alll Observable's:
Observable.zip(observableList, new FuncN<Object, Observable<?>>() {
#Override
public Observable<?> call(Object... args) {
return Observable.empty();
}
}).subscribe(new Subscriber<Object>() {
#Override
public void onCompleted() {
updateUserCache();
updateLocationCache();
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Object o) {
}
});
This is pseudocode, but I hope You understand the idea.
In case someone needs it, here's the code I used based on R. Zagórski suggestion:
List<Observable<?>> observableList = new ArrayList<>();
observableList.add(
getUsers()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(Observable::from)
.doOnNext(user->insertUser(user))
.toList()
);
observableList.add(
getLocations()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(Observable::from)
.doOnNext(location->insertLocation(location))
.toList()
);
Observable.zip(observableList, new FuncN<Object>() {
#Override
public Observable<?> call(Object...args) {
return Observable.empty();
}).subscribe(new Subscriber<Object>() {
#Override
public void onCompleted() {
updateUserCache();
updateLocationCache();
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Object o) {
}
});
.zip() is the proper way to do it
you may want to make Retrofit return Single instead of Observable though

RxJava - Nested Observables? (Retrofit)

I'm facing the problem that I need an authentication token to create my Retrofit service. I currently use an Observable to obtain said token, causing a rather ugly Observable construct:
Observable<MyService> observable = application.getMyService();
observable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(application.defaultSubscribeScheduler())
.subscribe(new Subscriber<MyService>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "Error creating service: ", e);
}
#Override
public void onNext(MyService myService) {
subscription = myService.searchStuff(searchFor)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(application.defaultSubscribeScheduler())
.subscribe(new Subscriber<AResponseWrapper>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable error) {
Log.e(TAG, "Error loading stuff: ", error);
}
#Override
public void onNext(AResponseWrapper wrapper) {
MainPresenter.this.stuff = wrapper.getStuff();
}
});
}
});
I can't help but feel that this is not how it should be done. Am I right?
The Observable.flatMap is what I was looking for.
It allows mapping the result to another observable:
Observable<MyService> observable = application.getMyService();
subscription = observable
.observeOn(application.defaultSubscribeScheduler())
.subscribeOn(application.defaultSubscribeScheduler())
.flatMap(service -> service.searchStuff(searchFor))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<AResponseWrapper>() {
#Override
public void onCompleted() {
if (series.size() < 1) {
mainView.showMessage(R.string.no_stuff_found);
} else {
mainView.showStuff(stuff);
}
}
#Override
public void onError(Throwable error) {
Log.e(TAG, "Error loading stuff: ", error);
}
#Override
public void onNext(AResponseWrapper wrapper) {
MainPresenter.this.stuff= wrapper.getStuff();
}
});
Note that I first observe on the IO Scheduler and only after the flatMap I'll subscribe on the main thread. Otherwise the service.searchStuff call (at least I think it's that part) would be executed on the Main thread, yielding a NetworkOnMainThreadException.
Thanks to #ahmed-ashraf-g who pointed me to this answer.

Avoid using CountDownLatch to wait for many threads before sending the callback

So I have a list of Track Ids that for each track Id I need to execute a network request to get the track details, I am using a for loop to launch all the requests and a latch to wait for all the requests to be completed. After they are completed then the callback is sent with the List of Tracks that have already populated.
I would like to know if there is any better way to do this, maybe with RxJava ?
I am using Retrofit 2.0 in Android.
public IBaseRequest batchTracksById(final TrackIdList trackIdListPayload, final IRequestListener<TracksList> listener) {
final TracksList tracks = new TracksList();
final Track[] trackArray = newrack[trackIdListPayload.getTrackIds().length];
tracks.setTrack(trackArray);
final CountDownLatch latch = new CountDownLatch(trackArray.length);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
latch.await();
handler.post(new Runnable() {
#Override
public void run() {
listener.onRequestUpdate(null, tracks, null, true);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
for (String id : trackIdListPayload.getTrackIds()) {
getTrackById(id, new IRequestListener<Track>() {
#Override
public void onRequestFailure(IBaseRequest request, Exception exception) {
latch.countDown();
}
#Override
public void onRequestUpdate(IBaseRequest request, Track track, RequestState state, boolean requestComplete) {
//iterate through the tracks and update the ones in the thing
int i = 0;
for (String s : trackIdListPayload.getTrackIds()) {
if (s.equals(track.getTrackId())) {
trackArray[i] = track;
// don't break here, as we may have a case where we have multiple instances of the same trackId (although
// at the moment a request will be made for each anyway...
}
i++;
}
latch.countDown();
}
});
}
return null;
}
If you want to make all the requests asynchronously and wait for them to return you can do this (lambdas for brevity and readability):
tracks.flatMap(track -> getTrackDetails(track.id)
.subscribeOn(Schedulers.io()))
.toList()
.doOnNext(list -> processTrackList())
...
If you require that the results are returned in the order of tracks but are still requested asynchronously then in soon to be released rxjava 1.0.15 you will be able to do this
tracks.concatMapEager(track -> getTrackDetails(track.id)
.subscribeOn(Schedulers.io())
.toList()
.doOnNext(list -> processTrackList())
...
If I understand correctly, you have a list of tracks as input and you want a list of webservice results. Here's a simple way to do that with RxJava if you can make your network call synchronous (rxjava will handle the background processing for you).
Observable.from(trackList)
.map(new Func1<Track, Response>() {
#Override
public Response call(Track track) {
return makeRequestSynchronously(track.id());
}
})
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<Response>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Response> responses) {
}
});
Edit: You can change Retrofit to return observables from webservice, if you do that you will need to change map to the following
.flatMap(new Func1<Track, Observable<Response>>() {
#Override
public Observable<Response> call(Track track) {
return makeRequestThatReturnsObservable(track.id());
}
})

Categories