Schedulers.io() not returning to main thread - android

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
}
});

Related

Why onNext() is updating textview, though observeOn(Schedulars.io()) on a different thread?

Observable.range(11,10).subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(new Observer<Integer>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
}
#Override
public void onNext(#NonNull Integer integer) {
textView.setText(String.valueOf(integer));
Log.d(TAG, "onNext: "+Thread.currentThread().getName());
}
#Override
public void onError(#NonNull Throwable e) {
}
#Override
public void onComplete() {
}
});
onNext() supposed to run on separate thread, but how is it updating textview, which is on main thread?
It seems that at the very beginning of the lifetime of a view, there is a very short timespan where you are able to change the view off the main thread.
As you started a thread off the main thread, directly in onCreate(), and this thread almost instantly returns a result (as there is no real work to do) you will not get a CalledFromWrongThreadException when you adjust the view.
If you put a short delay (maybe it is different on your machine) - for me, 50ms was enough - before the work in the thread / Observable starts, you will see the expected CalledFromWrongThreadException.
Observable.just("first")
.subscribeOn(Schedulers.newThread())
.delay(50, TimeUnit.MILLISECONDS)
.observeOn(Schedulers.newThread())
.subscribe(item -> {
textView.setText(item); // after the delay you will get a CalledFromWrongThreadException
});
And this is not related to RxJava. Creating a Thread which updates the view immediately shows the same behavior:
new Thread(new Runnable() {
#Override
public void run() {
textView.setText("foo"); // no CalledFromWrongThreadException
}
}).start();
Looks like this issue goes back to ViewRootImpl checkThread() which did not get called in this case. For further understanding follow the links below.
Despite, any change to a view should happen from the main thread. The scenario you have shown seems like a "lucky" side-effect.
Documentation
Android UI Not Crashing When Modifying View off UI Thread
Why is there no CalledFromWrongThreadException when a new thread operates UI immediately?
If you are using Data Binding Library, it allows to update the UI off the main thread.
You can change your data model in a background thread as long as it isn't a collection. Data binding localizes each variable / field during evaluation to avoid any concurrency issues.
observeOn is an async operation and after its processed , the result to pushed to onNext whixh is intended to run on the UI thread. They are changing the threading for us.Thats an advantage isnt? Its the feature of RxJava

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. (RxJava)

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

How to wait first request finish before start second with Rx?

I have an async method makeRequest() with callback. It called many times from different classes of my application. I need that this calls start one by one and never simultaneously.
I want to implement this using Rx. Like this:
public void execute() { // This method called many times from another classes
Observable.just(true)
// what I need to add here?
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(o -> {
internalExecute();
return o;
})
.subscribe();
}
private void internalExecute() { // This method should called only when previous call was finished
makeRequest(this::onRequestFinished);
}
private void onRequestFinished() {
// here is I handle request finish
}
But at now all requests works at parallel. What I need to add here to run requests one by one?
According to comments, you have here separated streams and requests. each client that execute request expect a result from the request. but no requests allowed to run in parallel, in this case I think the easiest way is to limit the Scheduler to an application global background sequential thread Executor, i.e:
Schedulers.from(Executors.newSingleThreadExecutor())
provide somewhere in your app this single thread Executor, in singleton manner of course, it's important that each request stream will use the same object:
private final Scheduler singleThreadScheduler = Schedulers.from(Executors.newSingleThreadExecutor());
public void execute() { // This method called many times from another classes
Observable.just(true)
.map(o -> {
internalExecute();
return o;
})
.subscribeOn(singleThreadScheduler)
.subscribe();
}
private void internalExecute() { // This method should called only when previous call was finished
makeRequest(this::onRequestFinished);
}
private void onRequestFinished() {
//NOTE: you should make sure that the callback execute where you need it (main thread?)
// here is I handle request finish
}
besides that, you're not exposing Observable outside, to the clients, but rather using callback mechanism, you can leverage reactive approach further, by making execute() returning Observable. (and enjoy composition of Obesrvables, operators, proper use of observeOn/subscribeOn, error handling with onError, disposing/unsubscribing etc.), as you're using async api, you can use fromEmitter()/create() (in newer RxJava1 version)), read more here:
private final Scheduler singleThreadScheduler = Schedulers.from(Executors.newSingleThreadExecutor());
public Observable<Result> execute() { // This method called many times from another classes
return Observable.fromEmitter(new Action1<Emitter<? extends Object>>() {
#Override
public void call(Emitter<?> emitter) {
emitter.setCancellation(() -> {
//cancel request on unsubscribing
});
makeRequest(result -> {
emitter.onNext(result);
});
}
})
.subscribeOn(singleThreadScheduler)
}

How would I run this statement using RxJava?

Rx way of doing things can be very complex for none and for many reasons...
but I feel there ARE simple ways to do simple things with RX...
How would I simply perform this statement on a background thread and receive the response on the ui thread?
All functions of this object need to run on a background thread. Get, put, clear, and delete.
String city = Paper.get("city");
The base object in Rx is Observable. That object usually wraps an OnSubscribe object, which is simply an extension of Action1 that takes a Subscriber as a parameter.
What all that means is that you just need to define a class that wraps your call and passes the result to the Subscriber:
public class RxPaperGet implements Observable.OnSubscribe<String> {
#Override
public void call(Subscriber<? super String> t1) {
try {
t1.onNext(Paper.get("city"));
} catch (Throwable t) {
t1.onError(t);
return;
}
t1.onCompleted();
}
}
That's a basic example. Now, you would want to wrap that so you can call any function, and not just Paper.get("city"). Something like https://github.com/ReactiveX/RxJavaAsyncUtil/blob/0.x/src/main/java/rx/util/async/operators/OperatorFromFunctionals.java#L44 does that, by allowing you to pass an arbitrary Callable.
Which in your case, would implement as:
Observable<String> res = OperatorFromFunctionals.fromCallable(() -> Paper.get("city"));
(In case you're wondering, this is java8 lambdas brought to android by retrolambda. quite nice to remove the verbosity of Rx)
Once you have your observable, you can subscribe on it, and get results. To execute on the background, and retrieve the results on the ui thread, you would do:
res.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
AndroidSchedulers is provided by rx-android.
Then you can simply be calledback with the result:
.subscribe(city -> Log.d(TAG, city));
That returns a subscription, which is useful if you need to cancel it.
Overall:
OperatorFromFunctionals.fromCallable(() -> Paper.get("city"))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(city -> Log.d(TAG, city));
EDIT: This is not correct. Will not delete the answer though to preserve the comments.
Very simple example:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getPaper()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
#Override
public void call(String s) {
Log.d("xxx", s);
}
});
}
private Observable<String> getPaper() {
return Observable.just(Paper.get());
}
where Paper.get() is a long running operation that returns a String. Check the docs for Scheduler.
Don't forget to observe on the main thread if you want to change the UI after receiving the result of your operation, else you will get an exception for changing the UI from outside the UI thread.

UI blocking occurs in Android despite RxJava

I am using RxJava to move network access to a separate thread in Android, but my UI still blocks.
I am not using the wrong observable as shown here: Android RxJava, Non Blocking?
The codepoints [A], [B] and [C] in below code are passed in the order [A] -> [C] -> [B] so the current thread is processed fine and RxJava calls [C] once it had a result. This is fine.
Also, blocking is much better compared to doing the network call on the UI thread, but I still have minor blocking. The UI stays fluent after the call is made, but if the server does not respond in a matter of milliseconds, it blocks.
private search; // search is an instance variable in the same class
// [A]
Observable.just(search.find("something")) // search.find calls the REST endpoint
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Search>() {
#Override public void call(Search search) {
// further processing // [B]
}
}, new Action1<Throwable>() {
#Override public void call(Throwable throwable) {
// error handler
}
});
// [C]
Could it be a problem that search is an instance variable in the same class where the Observable uses it, but the endpoint call is performed from a separate library? It shouldn't matter, right?
Am I doing anything bad that I shouldn't be doing?
--
Find looks like this (removed exception handling for brevity):
public Search find(String searchtext) {
setSearchtext(searchtext);
SearchEndpoint.find(Session.getUser().getId(), searchtext);
return this;
}
SearchEndpoint like this:
public static Search find(final Long userId, final String searchtext) throws IOException {
return ApiService.api().searches().find(userId).setFind(searchtext).execute();
}
and makes a call to the generated Google cloud endpoint library.
Try this:
Observable.create(new Observable.OnSubscribe<Search>() {
#Override
// method signature is from memory - I hope I am correct...
public void call(Subscriber<? super Search> subscriber) {
try {
Search search = search.find("something");
subscriber.onNext(search);
subscriber.onCompleted();
} catch (SomeException e) {
subscriber.onError(e);
}
}
})
// and then continue with your .subscribeOn(...)
To clarify, maybe this makes the problem with your code more obvious:
Observable.just(search.find("something"))
is clearly equivalent to
Search search = search.find("something");
Observable.just(search)
And this makes it obvious that search.find is executed before we ever hand the control over to rxjava and it is executed on whatever thread you are currently on - then the construction of an Observable from the pre-computed value and the delivery of the value happen on another thread but that does not help you much...
I know this is a few months old-- but instead of createing an entirely new Observable (which is relatively error-prone), you can use the map operator to run the search:
String search_input = "something"; // this is where you can specify other search terms
Observable.just(search_input)
.map(s -> search.find(s)) // search.find calls the REST endpoint
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe( // your subscriber goes here
If not using lambdas, that map function should look like:
.map(new Func1<String, Search>() {
#Override
public Search call(String s) {
return search.find(s)
}
})

Categories

Resources