I am new to RxJava2. In the code below, I am unable to understand how is the subscriber working on a background thread, even though the Observable/Flowable is emitting on the main thread and there is no Scheduler specified (using subscribeOn(Schedulers.*) calls). The full code can be found in this github repo.
#OnClick(R.id.btn_start_simple_polling)
public void onStartSimplePollingClicked() {
_log("onStartSimplePollingClicked called on "); //MAIN THREAD
final int pollCount = POLL_COUNT;
Disposable d = Observable
.interval(INITIAL_DELAY, POLLING_INTERVAL, TimeUnit.MILLISECONDS)
.map(this::_doNetworkCallAndGetStringResult)
.take(pollCount)
.doOnSubscribe(subscription -> {
_log(String.format("Start simple polling - %s", _counter)); //MAIN THREAD
})
.subscribe(taskName -> {
_log(String.format(Locale.US,
"Executing polled task [%s] now time : [xx:%02d]",
taskName,
_getSecondHand()));
});
_disposables.add(d);
}
private String _doNetworkCallAndGetStringResult(long attempt) {
try {
_log("_doNetworkCallAndGetStringResult called on "); //BACKGROUND THREAD
if (attempt == 4) {
// randomly make one event super long so we test that the repeat logic waits
// and accounts for this.
Thread.sleep(9000);
}
else {
Thread.sleep(3000);
}
} catch (InterruptedException e) {
Timber.d("Operation was interrupted");
}
_counter++;
return String.valueOf(_counter);
}
Since you did not specify a scheduler on which to subscribe RxJava defaults to a synchronous subscription. So the calls to onSubscribe and doOnSubscribe happen on the main thread.
However the Observable.interval operator requires either an implicit or an explicit scheduler to broadcast the onNext events. Since you did not specify a scheduler it defaults to Schedulers.computation().
After the interval fires it continues to call _doNetworkCallAndGetStringResult on the same computation thread, thus happening in the background.
RxJava by default run syncroniously but some operators as #Kiskae already told you as interval, delay or some others
If you want to run a pipeline asyncroniously you will have to use observerOn which will make run the pipeline in another thread once is put in your pipeline
/**
* Once that you set in your pipeline the observerOn all the next steps of your pipeline will be executed in another thread.
* Shall print
* First step main
* Second step RxNewThreadScheduler-2
* Third step RxNewThreadScheduler-1
*/
#Test
public void testObservableObserverOn() throws InterruptedException {
Subscription subscription = Observable.just(1)
.doOnNext(number -> System.out.println("First step " + Thread.currentThread()
.getName()))
.observeOn(Schedulers.newThread())
.doOnNext(number -> System.out.println("Second step " + Thread.currentThread()
.getName()))
.observeOn(Schedulers.newThread())
.doOnNext(number -> System.out.println("Third step " + Thread.currentThread()
.getName()))
.subscribe();
new TestSubscriber((Observer) subscription)
.awaitTerminalEvent(100, TimeUnit.MILLISECONDS);
}
or use subscribeOn which it will make your pipeline run in the thread that you specify
/**
* Does not matter at what point in your pipeline you set your subscribeOn, once that is set in the pipeline,
* all steps will be executed in another thread.
* Shall print
* First step RxNewThreadScheduler-1
* Second step RxNewThreadScheduler-1
*/
#Test
public void testObservableSubscribeOn() throws InterruptedException {
Subscription subscription = Observable.just(1)
.doOnNext(number -> System.out.println("First step " + Thread.currentThread()
.getName()))
.subscribeOn(Schedulers.newThread())
.doOnNext(number -> System.out.println("Second step " + Thread.currentThread()
.getName()))
.subscribe();
new TestSubscriber((Observer) subscription)
.awaitTerminalEvent(100, TimeUnit.MILLISECONDS);
}
You can see more examples about async rxJava here https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/scheduler/ObservableAsynchronous.java
Related
I want to be able to subscribe to publishsubject and wait for result, but no longer than 1 minute.
The problem is that if I do
publishsubject.timeout(1, TimeUnit.MINUTES).subscribe({result -> ... }, {error -> ... } )
I always get error even if before that I successfully get result. How to properly implement this approach?
You most likely get the timeout exception because timeout requires your source keeps producing items or completes within the specified time window. Thus, if you just signal one onNext to the PublishSubjectand never more, you'll get a timeout due to lack of a second onNext call.
So if you want one item, use take (before or after timeout):
publishsubject
.timeout(1, TimeUnit.MINUTES)
.take(1)
.subscribe(result -> { /* ... */ }, error -> { /* ... */ } )
In the exemple below I show how timeout works. For each emission, a new timeout is started and if a new item arrives before the timeout has ran the timeout is restated, otherwise an exception is thrown.
In the exemple, we can see 1, 2, 3 printing at console and it finish by a timeout exception because thE 4th item isn't here within the 200 milliseconds after the 3.
As I said in the comment below, you can avoid this if you know when you can terminate publishSubject. For exemple using take, takeUntil or calling publishSubject.onComplete() just after the 3rd item.
#Test
public void timeout() throws InterruptedException {
PublishSubject<Object> publishSubject = PublishSubject.create();
Observable<Object> timeout = publishSubject.timeout(200, TimeUnit.MILLISECONDS);
timeout
.subscribe(
e -> System.out.println(e),
error -> System.out.println("ERROR: " + error),
() -> System.out.println("complete")
);
sleep(50);
publishSubject.onNext(1);
sleep(150);
publishSubject.onNext(2);
sleep(199);
publishSubject.onNext(3);
sleep(201);
publishSubject.onNext(4);
Thread.sleep(2000);
}
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.
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.
The Problem
I have an activity which fetches data from an API periodically and displays the data received. The API uses OAuth so I receive a temporary access token which expires after a certain period of time (1 hr). If the app tries to get data with an expired token, obviously the request will fail. In an earlier iteration of my app, I was using AsyncTasks for the network requests and essentially just executed a new AsyncTask that would get a new access token before calling the main AsyncTask that fetches the data from the server. This worked great because the main AsyncTask would wait until the other one was finished before executing.
I recently switched to RxJava and basically just replaced the AsyncTasks with Observables. The problem is that the main Observable that fetches the data doesn't wait for the Observable that refreshes the access token to finish. Here's my code, thanks for your help.
Code
LiveThreadActivity.java
private Subscription subscription;
private Observable<List<CustomComment>> fetchData;
#Override
protected void onResume() {
super.onResume();
if (tokenExpired()) {
auth.refreshToken();
}
subscription = fetchData
.compose(bindToLifecycle())
.retryWhen(new RetryWithDelay(5, 2000))
.subscribe(list -> addNewComments(list), e -> handleFetchDataError(e));
}
// This method gets called in onCreate()
private void dataCollection() {
fetchData = Observable.interval(0, REFRESH_RATE, TimeUnit.MILLISECONDS)
.map(tick -> fetchNewComments()) // Run function every time a tick is emitted
.retryWhen( new RetryWithDelay(2, 2000) ) // Retry twice with 2 second delay
.subscribeOn(Schedulers.io()) // Network stuff in background thread
.observeOn(AndroidSchedulers.mainThread()); // Other stuff on the main thread
}
Auth.java
public class Auth {
...
public void refreshToken() {
Observable.just(1)
.map(y -> refreshAccessToken())
.retryWhen( new RetryWithDelay(3, 2000) )
.subscribeOn(Schedulers.io())
.subscribe();
}
}
Using reactive libraries a new way of thinking is needed. You have to write the code as it is synchronious, but be aware that it evecutes asynchroniously.
Your code just executes synchoniously. It executes two Observable's at the same time.
The function refreshToken() should look like:
public Observable<?> refreshToken() {
return Observable.just(1)
.map(y -> refreshAccessToken())
.retryWhen( new RetryWithDelay(3, 2000) )
.subscribeOn(Schedulers.io());
}
And onResume():
#Override
protected void onResume() {
super.onResume();
Observable obs = fetchData
.compose(bindToLifecycle())
.retryWhen(new RetryWithDelay(5, 2000));
if (tokenExpired()) {
obs = obs.startWith(auth.refreshToken());
}
subscription = obs
.subscribe(list -> addNewComments(list), e -> handleFetchDataError(e));
}
Notice startWith() operator. It allows to executes one Observable (fetching list) after another (refreshing token).
.flatMap() will probably be sufficient, i.e. tokenObservable.flatMap(/* return dataObservable */)
I really like the RxJava, it's a wonderful tool but somethimes it's very hard to understand how it works.
We use Retrofit with a RxJava in our Android project and there is a following use-case:
I need to poll the server, with some delay between retries, while server is doing some job. When server is done I have to deliver the result. So I've successfully done it with RxJava, here is the code snippet:
I used "skipWhile" with "repeatWhen"
Subscription checkJobSubscription = mDataManager.checkJob(prepareTweakJob)
.skipWhile(new Func1<CheckJobResponse, Boolean>() {
#Override
public Boolean call(CheckJobResponse checkJobResponse) {
boolean shouldSkip = false;
if (SHOW_LOGS) Logger.v(TAG, "checkJob, skipWhile, jobStatus " + checkJobResponse.getJobStatus());
switch (checkJobResponse.getJobStatus()){
case CheckJobResponse.PROCESSING:
shouldSkip = true;
break;
case CheckJobResponse.DONE:
case CheckJobResponse.ERROR:
shouldSkip = false;
break;
}
if (SHOW_LOGS) Logger.v(TAG, "checkJob, skipWhile, shouldSkip " + shouldSkip);
return shouldSkip;
}
})
.repeatWhen(new Func1<Observable<? extends Void>, Observable<?>>() {
#Override
public Observable<?> call(Observable<? extends Void> observable) {
if (SHOW_LOGS) Logger.v(TAG, "checkJob, repeatWhen " + observable);
return observable.delay(1, TimeUnit.SECONDS);
}
}).subscribe(new Subscriber<CheckJobResponse>(){
#Override
public void onNext(CheckJobResponse response) {
if (SHOW_LOGS) Logger.v(TAG, "checkJob, onSuccess, response " + response);
}
#Override
public void onError(BaseError error) {
if (SHOW_LOGS) Logger.v(TAG, "checkJob, onError, canEditTimeline, error " + error);
Toast.makeText(ChoseEditOptionActivity.this, R.string.NETWORK__no_internet_message, Toast.LENGTH_LONG).show();
}
#Override
public void onCompleted() {
if (SHOW_LOGS) Logger.v(TAG, "onCompleted");
}
});
The code works fine:
When server responded that job is processing I return "true" from "skipWhile" chain, the original Observable waits for 1 second and do the http request again.
This process repeats until I return "false" from "skipWhile" chain.
Here is a few things I don't understand:
I saw in the documentation of "skipWhile" that it will not emit anything (onError, onNext, onComplete) from original Observable until I return "false" from its "call" method. So If it doesn't emit anything why does the "repeatWhen" Observable doing it's job? It waits for one second and run the request again. Who launches it?
The second question is: Why Observable from "repeatWhen" is not running forever, I mean why it stops repeating when I return "false" from "skipWhile"? I get onNext successfully in my Subscriber if I return "false".
In documentation of "repeatWhile" it says that eventually I get a call to "onComplete" in my Subscriber but "onComplete" is never called.
It makes no difference if I change the order of chaining "skipWhile" and "repeatWhen". Why is that ?
I understand that RxJava is opensource and I could just read the code, but as I said - it's really hard to understand.
Thanks.
I've not worked with repeatWhen before, but this question made me curious, so I did some research.
skipWhile does emit onError and onCompleted, even if it never returns true before then. As such, repeatWhen is being called every time checkJob() emits onCompleted. That answers question #1.
The rest of the questions are predicated on false assumptions. Your subscription is running forever because your repeatWhen never terminates. That's because repeatWhen is a more complex beast than you realize. The Observable in it emits null whenever it gets onCompleted from the source. If you take that and return onCompleted then it ends, otherwise if you emit anything it retries. Since delay just takes an emission and delays it, it's always emitting the null again. As such, it constantly resubscribes.
The answer to #2, then, is that it is running forever; you're probably doing something else outside this code to cancel the subscription. For #3, you never get onCompleted because it never completes. For #4, the order doesn't matter because you're repeating indefinitely.
The question now is, how do you get the correct behavior? It's as simple as using takeUntil instead of skipWhile. That way, you keep repeating until you get the result you want, thus terminating the stream when you want it to end.
Here's a code sample:
Observable<Boolean> source = ...; // Something that eventually emits true
source
.repeatWhen(completed -> completed.delay(1, TimeUnit.SECONDS))
.takeUntil(result -> result)
.filter(result -> result)
.subscribe(
res -> System.out.println("onNext(" + res + ")"),
err -> System.out.println("onError()"),
() -> System.out.println("onCompleted()")
);
In this example, source is emitting booleans. I repeat every 1 second until the source emits true. I keep taking until result is true. And I filter out all notifications that are false, so the subscriber doesn't get them until it's true.