I had a method calling a webservice which I thought was running on IO thread until the service stopped and the UI froze.
So I started some simple testing to check threading
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'io.reactivex.rxjava2:rxjava:2.1.8'
public void test() {
disposableRx.add(
Observable.just(1, 2)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(new Consumer<Integer>() {
#Override
public void accept(Integer integer) throws Exception {
System.out.println("Emitting item on: " + Thread.currentThread().getName());
}
})
.map(new Function<Integer, Integer>() {
#Override
public Integer apply(#NonNull Integer integer) throws Exception {
System.out.println("Processing item on: " + Thread.currentThread().getName());
return integer * 2;
}
})
.subscribeWith(new DisposableObserver<Integer>() {
#Override
public void onNext(#NonNull Integer integer) {
System.out.println("Consuming item on: " + Thread.currentThread().getName());
}
#Override
public void onError(#NonNull Throwable e) {
}
#Override
public void onComplete() {
}
})
);
}
Is resulting in the following output indicating everything is running on the main thread, despite having subscribe and observe ?
Emitting item on: main
Processing item on: main
Consuming item on: main
Emitting item on: main
Processing item on: main
Consuming item on: main
BUT
If I move the observeOn to immediately before the .subscribeWith as follows...
public void test() {
disposableRx.add(
Observable.just(1, 2)
.subscribeOn(Schedulers.io())
.doOnNext(new Consumer<Integer>() {
#Override
public void accept(Integer integer) throws Exception {
System.out.println("Emitting item on: " + Thread.currentThread().getName());
}
})
.map(new Function<Integer, Integer>() {
#Override
public Integer apply(#NonNull Integer integer) throws Exception {
System.out.println("Processing item on: " + Thread.currentThread().getName());
return integer * 2;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<Integer>() {
#Override
public void onNext(#NonNull Integer integer) {
System.out.println("Consuming item on: " + Thread.currentThread().getName());
}
#Override
public void onError(#NonNull Throwable e) {
}
#Override
public void onComplete() {
}
})
);
}
The output is what I am looking for which I must say is confusing me even after reading many blogs about RxJava.
Emitting item on: RxCachedThreadScheduler-1
Processing item on: RxCachedThreadScheduler-1
Emitting item on: RxCachedThreadScheduler-1
Processing item on: RxCachedThreadScheduler-1
Consuming item on: main
Consuming item on: main
I've stripped my original method back until it's pretty much a copy of an example method on a Blog post
Multithreading like a boss
which implies that this should run the loadPerson() on the IO thread, emitting on the main thread. It doesn't.
disposableRx.add(
repo.loadPersonProfile(id).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableMaybeObserver<String>() {
#Override
public void onSuccess(#NonNull String response) {
loadPersonDetailsResponse.setValue(ViewModelResponse.success(response));
isLoading.setValue(false);
}
#Override
public void onError(#NonNull Throwable e) {
loadPersonDetailsResponse.setValue(ViewModelResponse.error(e));
isLoading.setValue(false);
}
#Override
public void onComplete() {
}
})
);
Dumping out the thread from within my method shows that it's running on the Main thread?
What's causing this?
The order in which you put observeOn() and sunbscribeOn() , and other operators is very important.
subscribeOn() operator tells the source Observable which thread to emit and transform items on.
Be careful where you put the observeOn() operator because it changes
the thread performing the work! In most cases you probably want to delay
switching to the observing thread until the very end of your Rx chain.
Observable.just("long", "longer", "longest")
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.map(String::length)
.filter(length -> length == 6)
.subscribe(length -> System.out.println("item length " + length));
here the map, filter and consuming is performed in the main thread
observeOn() before map()
There is no reason to have observeOn() operator applied above the map() operator.
In fact, this code will result in NetworkOnMainThreadException! We do not want to be reading from HTTP response on the main thread — it should be done before we switch back to the main thread.
you can also use multiple observeOn() to switch threads like this example.
Observable.just("long", "longer", "longest")
.doOnNext(s -> System.out.println("first doOnNext: processing item on thread " + Thread.currentThread().getName()))
.observeOn(Schedulers.computation())
.map(String::toString)
.doOnNext(s -> System.out.println("second doOnNext: processing item on thread " + Thread.currentThread().getName()))
.observeOn(Schedulers.io())
.map(String::toString)
.subscribeOn(Schedulers.newThread())
.map(String::length)
.subscribe(length -> System.out.println("received item length " + length + " on thread " + Thread.currentThread().getName()));
OUTPUT :
first doOnNext: processing item on thread RxNewThreadScheduler-1
first doOnNext: processing item on thread RxNewThreadScheduler-1
first doOnNext: processing item on thread RxNewThreadScheduler-1
second doOnNext: processing item on thread RxComputationThreadPool-1
second doOnNext: processing item on thread RxComputationThreadPool-1
second doOnNext: processing item on thread RxComputationThreadPool-1
received item length 4 on thread RxCachedThreadScheduler-1
received item length 6 on thread RxCachedThreadScheduler-1
received item length 7 on thread RxCachedThreadScheduler-1
Note
according to this answer subscribeOn() does't apply to the downstream operators, Therefore it does not guarantee that your operation is going to be on a different Thread.
subscribeOn effects go upstream and closer to the source of
events.
As for your problem I have made a test and here are the results
private void testRxJava2Async() {
io.reactivex.Observable.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
Log.d(TAG,"callable (expensive assync method) was called on --> "+Thread.currentThread().getName());
return null;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String s) {
Log.d(TAG,"onNext() was called on --> "+Thread.currentThread().getName());
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
}
Test Result :
callable (expensive assync method) was called on --> RxCachedThreadScheduler-1
onNext() was called on--> main
Believe me i understand your confusion, let me explain step by step. Remember this for every subscribe there should be observe. We all know all subscribe should be executed on a worker thread(IO) and observer on Main thread(UI). In the first example the subscribe fired, then you said, for this subscription all updates will be delivered on the main thread, regardless of any more subscriptions during any transformations. However in the second example , this is what you said , observe only when the map transformations on the subscriptions is transformed then run on Main thread. Think of it, as Pre and Post increment in programming.
Hope this makes sense. RX framework is great. but actually requires live practice to get it right. Has you have seen first hand.
To ensure your changes are pushed and performed on the main thread what you need to do is to add a intermediary step to observe the changes.
Integer[] list = {6,3,2,1};
Observable.just(list).subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.newThread())
.map(value = > value * 2)
.observeOn(Schedulers.mainThread())
.subscribeWith(...)
The idea is this subscribeOn only accepts to start the processing , such as a NetworkRequest but does not guarantee that values will be pushed to that same thread. observeOn says hey i can receive those values you initially subscribed for. So to make sure the values are transformed on the main thread, you can observe changes on another plain thread , perform a operation(Map| Flatmap etc), then observe those changes this will guarantee only those values are placed on the main thread. Basically what your saying is this, hey perform all the heavy processing in those threads, but hey whenever the computations are done, pass me those values to me in the main thread to continue my work, only then can i accept, therefore no work will ever be done on the main thread.
Related
I found many articles regarding the error I am getting, I know we can only update UI from the main thread only. Let me tell you the whole error I am getting:
E/RxEroor: The exception could not be delivered to the consumer because it has already cancelled/disposed of the flow or the exception has nowhere to go, to begin with. Further reading: https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling | android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
I am getting the error by using this:
RxJavaPlugins.setErrorHandler(throwable -> {
Log.e("RxEroor",throwable.getLocalizedMessage());
});
I am using rxjava to observe things from a retrofit network call. Here is my call to ViewModel from fragment to fetch data.
compositeDisposable.add(songsOfCategoryViewModel.getAllSongs(1)
.subscribeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<ServerResponse<JsonObject>>() {
#Override
public void onSuccess(#io.reactivex.rxjava3.annotations.NonNull ServerResponse<JsonObject> jsonObjectServerResponse) {
if (jsonObjectServerResponse.isStatus()){
Log.i("Songs Of Category",jsonObjectServerResponse.getData().toString());
List<SongModel> serverSongList = new Gson()
.fromJson(jsonObjectServerResponse.getData().get("songs")
.getAsJsonArray(),new TypeToken<List<SongModel>>(){}.getType());
localSongList.addAll(serverSongList);
songAdapter.notifyDataSetChanged();
/* Observable
.fromIterable(serverSongList)
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(new DisposableObserver<SongModel>() {
#Override
public void onNext(#io.reactivex.rxjava3.annotations.NonNull SongModel songModel) {
Log.i("Song",songModel.getTitle());
localSongList.add(songModel);
songAdapter.notifyDataSetChanged();
}
#Override
public void onError(#io.reactivex.rxjava3.annotations.NonNull Throwable e) {
}
#Override
public void onComplete() {
Log.i("onComplete","Called");
}
});
*/
}else {
Log.e("Error",jsonObjectServerResponse.getMessage());
}
}
#Override
public void onError(#io.reactivex.rxjava3.annotations.NonNull Throwable e) {
Log.e("Error",e.getMessage());
}
}));
As you can see I have provided AndroidSchedulers.mainThread() to subscribeOn. I m getting the data on onSucces method but recyclerview adapter not updating it on UI.
And let me tell you most tricky part: if I switch the app to the foreground by locking the screen or pressing the home button and return to the app, my UI get updated with the data which I have received.
You should be using observeOn(AndroidSchedulers.mainThread()) not subscribeOn(AndroidSchedulers.mainThread()) . Use as
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
subscribeOn used to specify the Scheduler on which an Observable will operate. In your case it will be an IO scheduler.
ObserveOn is used specify the Scheduler on which an observer will observe this Observable i.e the completion in this case it will be Main thread
I'm learning RxJava2 and I need to chain three observables:
The first one performs operations on the data:
Completable performOperations(Data data); // performs expensive operations.
The second one uploads data to a server1 and emits percentage progress.
Observable<Integer> uploadToServer1(Data data); // while it performs the upload, it calls several times onNext(progress) and finally calls onComplete().
The third one just informs to a server2 that the upload was done.
Completable informUploadedToServer2(Data data); // just calls a REST API.
I would like to show the progress in my Activity of the second observable and finally show success when the third one finishes successfully. If any of the three observables throws an exception I should show the error in the Activity as well.
I've tried to use concat to chain but it won't compile because uploadToServer1 emits the Integer type and the rest doesn't.
public void upload(Data data, MyCallback callback) {
Observable.concat(performOperations(data).toObservable(), uploadToServer1(data), informUploadedToServer2(data))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new DisposableObserver<Integer>() {
#Override
public void onNext(Integer integer) {
callback.onProgressChanged(integer);
}
#Override
public void onError(Throwable e) {
callback.onError();
}
#Override
public void onComplete() {
callback.onSuccess();
}
});
}
I've seen that if I change to
Observable.concat(performOperations(data).<Integer>toObservable(), uploadToServer1(data), informUploadedToServer2(data).<Integer>toObservable())
it will work, however, is this the recommended approach?
What is more, what if the first observable emits non-Integers?, for example, a DataDiff object which would describe the modification after a certain operation was performed:
Observable<DataDiff> performOperations(Data data);
How should I subscribe so that I can listen for onNext(Integer) and also onNext(DataDiff) so that the Activity can update the view accordingly?
Thanks.
I would do that in a different way, a more "streamy" approach.
First performOperations(), then use andThen operator to concatenate with the Observable<Integer>, and then you can use concatWith so that after that all the elements from the Observable<Integer> are emitted informUploadedToServer2 is executed. You can then handle the Integer emitted in the subscription consumer, if you observeOn(AndroidSchedulers.mainThread) you can than safely notify your Activity there
performOperations(data)
.andThen(uploadToServer1(data))
.concatWith(informUploadedToServer2(data))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Integer>() {
#Override
public void accept(Integer integer) throws Exception {
// notify your Activity here
}
});
In case you needed to intercept the completion of one of the streams, you could use doOnComplete, for instance
performOperations(data)
.doOnComplete(new Action() {
#Override
public void run() throws Exception {
// after performOperations has completed but before
// uploadToServer1 has started
}
})
.andThen(uploadToServer1(data))
// ...
In case performOperations() returned an Observable<DataDiff>, you could use doOnNext to intercept all the events, and then use ignoreElements operator to convert it to a Completable and then use andThen as before
performOperations()
.doOnNext(new Consumer<DataDiff>() {
#Override
public void accept(DataDiff dataDiff) throws Exception {
// handle DataDiff here
}
})
.ignoreElements()
.andThen(uploadToServer1())
// ...
I am researching into RxJava/RxAndroid. I decided to create Observable via Observable.fromCallable method because of it gives us two important things:
The code for creating the emitted value is not run until someone subscribes to the Observerable
The creation code can be run on a different thread.
I have wrote the sample as below:
private void testObservableFromCallable() {
Observable<String> stringObservable = Observable.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
return "Hello";
}
});
Subscription subscription = stringObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String s) {
Log.d("LOG_TAG", "Thread name: " + Thread.currentThread().getName());
textView.setText(s);
}
});
}
Every things are ok. It work fine and log tag show thread name is main. It means onNext() method be called on mainThread() and textView be accessed properly.
Now what am I confusing is when I remove this line observeOn(AndroidSchedulers.mainThread()) or just change it to observeOn(Schedulers.io()). Both of two cases the log tag show thread name is RxIoScheduler. That is not main thread, but the app compile with no error and textView show the text "Hello" on the screen.
Anyone help me explain why am I able to access to the view outside main thread?.
Thanks so much!
The framework usually does not do anything to check that you're really on the main thread. As a caller, it's your responsibility.
Some but not all UI widget access on non-main thread lead to a crash. Here you're being "lucky" that the thread problem goes undetected with your method call.
I wrote a method to print the output from flatMap (Pseudo code):
Observable.just(...).repeat()
.flatMap( return Observable.just([double]))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Double>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
tvConfidence.setText(e.getMessage());
}
#Override
public void onNext(Double aDouble) {
tvConfidence.setText("Confidence :" + aDouble);
}
});
When I run these code, it works a few seconds but after a few seconds, it would not run onto the onNext method again. I don't know why, because I debug the code, it will run the Observable.just(double), and the value always changed but it would not execute the code setText to refresh the textView.
My guess is that due to that particular flatMap overload, you eventually start to accumulate a lot of just because flatMap is unbounded-in. Try with flatMap(f, 1) to limit the concurrency level.
I'm using RxParse to parse query's async load but when i subscribe my observable using subscribeOn(Schedulers.io()) my onCompleted method is never called on main thread. Instead of this, my onCompleted method is called inside of worker thread pool. If i use observeOn(AndroidSchedulers.mainThread) everything will work as well, but my onNextMethod will be called on main thread too and I don't want it.
There is something wrong in my code?
Have anything wrong in my code?
ParseObservable.find(myQuery)
.map(myMapFunc())
.subscribeOn(AndroidSchedulers.handlerThread(new Handler()))
.subscribe(
new Subscriber<MyObj>() {
#Override
public void onError(Throwable e) {
Log.e("error","error",e);
}
#Override
public void onNext(T t) {
// ... worker thread (but here is ok)
}
public void onCompleted() {
// ... worker thread again instead of mainThread
}
}
)
);
First you need to understand the difference between subscribeOn() and observeOn(). These are two completely different operators that affect different parts of the Rx chain.
subscribeOn() specifies where your Observable will do its work. It will not affect where onNext(), onError(), and onComplete() execute.
observeOn() specifies where the the callbacks (e.g. onNext()) are executed. It will not affect where your Observable does its work.
All the callbacks will occur on the same thread. You cannot specify that some callbacks occur on one thread and some happen on another through any RxJava APIs. If that is the behavior you desire, you will have to implement it yourself in your callbacks.
Unfortunately the subscription is in the same thread for all methods (onNext, onError and onCompleted
But you can observe in the Schedulers.io() and inside the onNext(T t) method, create a new Observable to listen in the MainThread like this:
ParseObservable.find(myQuery)
.map(myMapFunc())
.subscribeOn(Schedulers.io())
.subscribe(
new Subscriber<MyObj>() {
#Override
public void onError(Throwable e) {
Log.e("error","error",e);
}
#Override
public void onNext(T t) {
Observable.just(t)
.observeOn(AndroidSchedulers.mainThread())
.subscribe((t) -> {
// do something in MainThread
})
}
public void onCompleted() {
// ... worker thread again instead of mainThread
}
}
)
);
I hope it help!
I would recommend using "side action" operators in this case. It seems to me like a slightly more elegant solution than using nested observables:
ParseObservable.find(myQuery)
.map(myMapFunc())
.subscribeOn(AndroidSchedulers.handlerThread(new Handler()))
.doOnCompleted(() -> onCompleteAction())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(value -> onNext(value))
.subscribe();
It is not advisable to subscribe within a subscription.
subscribeOn determines where the Observable chain will start when an observer subscribes to it.
observeOn can be used at different points (and multiple times, if need be) throughout your observable chain to pass control between threads. (You can verify this by checking whether you're on the main thread or not within each of these blocks).
ParseObservable.find(myQuery)
.map(myMapFunc())
// Added this:
.doOnNext(obj -> {
// NOTE: This will happen on your `subscribeOn` scheduler
// Do something with `obj` here while on worker thread
}
.subscribeOn(AndroidSchedulers.handlerThread(new Handler()))
// Added this:
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<>() {
next -> {
// NOTE: This will happen on the main thread
},
error -> {
Log.e("error","error",e);
// NOTE: This will happen on the main thread
},
() -> {
// NOTE: This will happen on the main thread
}
});