Operators after findallAsync().asFlowable() are running on the UI thread - android

I'm facing with a problem, rxjava operators are running in the UI thread.
I'm using findAllAsync() to get object asynchronously and using asFlowable() to treat them with rxjava operator.
realm.where(Specie.class)
.equalTo("fauna", true)
.findAllAsync().asFlowable()
.filter(new Predicate<RealmResults<Specie>>() {
#Override
public boolean test(RealmResults<Specie> species) throws Exception {
System.out.println("THREAD : " + Thread.currentThread().getId()); // Print 1
return species.isLoaded();
}
})
But in the realm rxjava example they are using observeOn(AndroidSchedulers.mainThread()) , it means the previous operators are running asynchronously otherwise it would be useless.
Link : https://github.com/realm/realm-java/blob/master/examples/rxJavaExample/src/main/java/io/realm/examples/rxjava/animation/AnimationActivity.java
disposable = realm.where(Person.class).findAllAsync().asFlowable()
.flatMap(persons -> Flowable.fromIterable(persons))
.zipWith(Flowable.interval(1, TimeUnit.SECONDS), (person, tick) -> person)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(person -> {
TextView personView = new TextView(AnimationActivity.this);
personView.setText(person.getName());
container.addView(personView);
});
How can I run operators after asFlowable() asynchronously ?
edit : How can I access RealmResults obtained on the UI thread on a
background thread ?

The execution on Schedulers.computation(), so they add observeOn(AndroidSchedulers.mainThread()) to go back to the main thread.
The Realm Async Query API handles the asynchronous evaluation of the query on its own, and the results of that query are passed back by Realm to the UI thread, that is when isLoaded() is true.
To get off the main thread, you can use observeOn(Schedulers.io()) for example.

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

How to properly implement camera2 realtime frame processing using RxJava?

I'm making reactive wrapper over camera2, my goal is to get each frame and then pass to face recognition.
So, I created a wrapper method over setOnImageAvailableListener
fun createOnImageAvailableFlowable(imageReader: ImageReader, handler: Handler): Flowable<ImageReader> {
return Flowable.create({ subscriber ->
imageReader.setOnImageAvailableListener({
if (!subscriber.isCancelled)
subscriber.onNext(it)
}, handler)
subscriber.setCancellable {
imageReader.setOnImageAvailableListener(null, null)
}
}, BackpressureStrategy.LATEST)
}
Reactive chain looks as follows:
createOnImageAvailableFlowable(imageReader!!, null)
.concatMap {
it.acquireLatestImage()?.use { image ->
val rotation = ReactiveCamera.getRotationCompensation(cameraId!!, this, applicationContext)
val visionImage = FirebaseVisionImage.fromMediaImage(image, rotation)
firebaseFaceDetector
.detectInImage(visionImage)
.toFlowable(BackpressureStrategy.LATEST)
.map { list ->Optional(list)}
} ?: Flowable.just(Optional(null))
}
...
This code works, but cause some lags on preview surface because all work performed in the main thread. This needs to be performed in separate thread. My naive solution is to add observeOn operator before concatMap:
createOnImageAvailableFlowable(imageReader!!, null)
.observeOn(Schedulers.io()) // doesn't switch thread
.concatMap {
// still main thread
}
...
But it doesn't affect, all work still in the main thread. If I specify concatMapEager instead of concatMap, all works as expected in separate thread, but the frames comes with a significant delay.
What I'm doing wrong? How can I instruct the reactive stream to be performed in a separate thread in this case? How can backpressure be handled in case of realtime frame processing?
Upd
I provided my own thread as Kiskae suggested, but now, only first emission happens in scheduler's thread, but the rest emissions remain in the main thread:
createOnImageAvailableFlowable(imageReader!!, null)
.subscribeOn(AndroidSchedulers.from(nonMainThread.looper))
.concatMap {
val t = Thread.currentThread()
val name = t.name
Log.d(TAG, "current thread {$name}")
...
}
Output:
D/MainActivity: current thread {Camera2}
D/MainActivity: current thread {main}
D/MainActivity: current thread {main}
D/MainActivity: current thread {main}
D/MainActivity: current thread {main}
Looking at the documentation of ImageReader.setOnImageAvailableListener:
Handler: The handler on which the listener should be invoked, or null if the listener should be invoked on the calling thread's looper.
Since you're subscribing on the main looper it ends up setting up the callback using the main looper, this causes all the processing before the concatMap to always occur on the application thread.
You can solve this by either providing a handler instead of null or calling subscribeOn and providing a handler-based scheduler like RxAndroid's HandlerScheduler.

What is the alternative to AndroidSchedulers.mainThread() in RxJava?

Is there a Scheduler api in RxJava synonymous to AndroidSchedulers.mainThread() in RxAndroid.
So If I schedule a task on a new thread and I want to observe it on Java Main Thread, how would I do that?
edit
Below is an example RxSubscription, with system.in commented, the Main thread is killed while the Observable.interval runs on a separate thread. In Android, I can say observeOn(AndroidSchedulers.MainThread) and any operation thereafter would run on the main thread. I am looking for a similar scheduler in Java as AndroidSchedulers is part of RxAndroid.
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import rx.Observable;
public class Main {
public static void main(String[] args) throws InterruptedException, IOException {
Observable<Long> values = Observable.interval(1000, TimeUnit.MILLISECONDS);
values.subscribe(
v -> System.out.println("Received: " + v),
e -> System.out.println("Error: " + e),
() -> System.out.println("Completed")
);
//System.in.read();
}
}
Getting back to the "main" Java thread is currently not possible as there is no blocking Scheduler for RxJava 1.x.
In case you can upgrade to RxJava 2.x, I have a special Scheduler that can be "pinned" to the current thread:
compile "com.github.akarnokd:rxjava2-extensions:0.15.1"
BlockingScheduler
This type of scheduler runs its execution loop on the "current thread", more specifically, the thread which invoked its execute() method. The method blocks until the shutdown() is invoked. This type of scheduler allows returning to the "main" thread from other threads.
public static void main(String[] args) {
BlockingScheduler scheduler = new BlockingScheduler();
scheduler.execute(() -> {
Flowable.range(1, 10)
.subscribeOn(Schedulers.io())
.observeOn(scheduler)
.doAfterTerminate(() -> scheduler.shutdown())
.subscribe(v -> System.out.println(v + " on " + Thread.currentThread()));
});
System.out.println("BlockingScheduler finished");
}
Yes, RxJava has schedulers. To send a message to any thread, you need to have a message loop of some sort waiting for messages from the other threads. In Android this is your Looper. In Java, you'd need to do that yourself. Your Scheduler would then send a message to that thread and do the work in that message response. The mechanism for that depends on how you implement your message queue, but should be fairly trivial.

Schedulers.io() not returning to main thread

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

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.

Categories

Resources