RxJava combine publishsubject with timeout - android

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

Related

Execute code just before onComplete() in RxJava 2?

I need to close socket connection in my observable before RxLifecycle dispose it. How can I do that?
if you want to do an action after all, just before the subscriber unsubscribe from the observable you can use operator doOnUnsubscribe
#Test
public void testDoOnUnsubscribe() {
Integer[] numbers = {0, 1, 2, 3, 4};
Observable.from(numbers)
.doOnUnsubscribe(() -> System.out.println("Last action must be done here"))
.subscribe(number -> System.out.println("number:" + number),
System.out::println,
() -> System.out.println("End of pipeline"));
}
It should print in this order
number:0
number:1
number:2
number:3
number:4
End of pipeline
Last action must be done here
You could try using doFinally
Calls the specified action after this Observable signals onError or onCompleted or gets disposed by the downstream.
http://reactivex.io/RxJava/javadoc/io/reactivex/Observable.html#doFinally-io.reactivex.functions.Action-
one can try this too in case if you're iterating objects using filter and map for combining result.
.doOnTerminate(() -> Log.d(LOGGER, "terminated"))

Query on RxJava thread scheduling

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

RxJava emit only when emitter stops

In an Android app, I'd like to refresh the list only once the user has stopped selecting a list of items in a List. So in effect, I'd like to the observer to be informed only once the producer has stopped emitting for at least 500ms.
Right now I have something like the following:
Subject<Object> _bus = PublishSubject.create().toSerialized();
...
_bus.onNext(new Event());
...
_bus.delay(500, TimeUnit.MILLISECONDS)
.distinctUntilChanged()
.observeOn(Schedulers.computation())
.subscribe(event -> {
// Do something with event
}));
This is fine, except it emits at 500 ms intervals even if the source is still emitting. I'd like to wait for 500ms to see if the source has stopped calling onNext() and only then emit.
Is this possible?
So basically you need debouncing with buffer. There is article which should helper you.
And kick off sample from that article:
Observable<Object> tapEventEmitter = _rxBus.toObserverable().share();
Observable<Object> debouncedEventEmitter = tapEventEmitter.debounce(1, TimeUnit.SECONDS);
Observable<List<Object>> debouncedBufferEmitter = tapEventEmitter.buffer(debouncedEventEmitter);
debouncedBufferEmitter.buffer(debouncedEventEmitter)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<Object>>() {
#Override
public void call(List<Object> taps) {
_showTapCount(taps.size());
}
});
I think you have to used debounce operator instead of delay eg.
_bus.debounce(500, TimeUnit.MILLISECONDS
.distinctUntilChanged()
.observeOn(Schedulers.computation())
.subscribe(event -> {
// Do something with event
}));

Send correct current time after a retry RxJava and Retrofit

I'm trying to send to the server the current time at the time the connection is established. I already know that this time may have some latency, regarding the health of the network.
For this, I'm using retrofit(2) and rxjava(1), and I have some delay between retries and a retry count, something like this:
mRetrofit = new ApiClient().getObservableClient();
mService = mRetrofit.create(ApiObservableService.class);
mService.sendServerTimeNow(getCurrentTime())
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.retryWhen(«new RetryWithDelay(retries, delay))
.subscribe(new CustomSubscriber<ResponseBody>());
My problem is, every time the retry is made, the getCurrentTime() is not refreshed and its value is always the same on the time of subscription.
E.g.
Retry 1 - 1482399029862
Retry 2 - 1482399029862 //here should be changed to the new current time
Retry 3 - 1482399029862 //here should be changed to the new current time
I had the felling that retry would re-subscribe, and if this is true is not suppose to refresh the current time?
This is my getCurrentTime()
public long getCurrentTime(){
return System.currentTimeMillis();
}
How can I accomplish this refresh of current time?
Additionally, I already tried but with no success:
Observable.just(getCurrentTime())
.flatMap(new Func1<Long, Observable<ResponseBody>>() {
#Override
public Observable<ResponseBody> call(Long aLong) {
mService.sendServerTimeNow(aLong)
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.retryWhen(«new RetryWithDelay(retries, delay))
.subscribe(new CustomSubscriber<ResponseBody>());
You should first understand that Observable.just(getCurrentTime()) executes getCurrentTime() when the Observable sequence is assembled and doesn't make the call "magically" deferred:
final long now = getCurrentTime();
Observable<Long> o = Observable.just(now);
o.test().assertResult(now); // "now" is set in stone inside the Observable
o.test().assertResult(now);
o.test().assertResult(now);
You can instead have Observable.fromCallable(() -> getCurrentTime()) which will only call getCurrentTime for each incoming subscriber, including the one by retry.
Let's look at your example with Observable.just :
Observable.just(getCurrentTime())
.flatMap(...)
...
can be rewritten as follow :
Long time = getCurrentTime()
Observable.just(time)
.flatMap(...)
...
A retry will subscribe again to your Observable and will reuse the value of time.
To compute the value again, you'll have to compute the time again, using Observable.fromCallable for example
Observable.fromCallable(() -> getCurrentTime())
.flatMap(...)
...

Using of "skipWhile" combined with "repeatWhen" in RxJava to implement server polling

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.

Categories

Resources