I'm trying to add a delay in between each batch write and I managed to get it working by modifying this example but I'm not sure this is the correct way to achieve this?
rxBleConnection.createNewLongWriteBuilder()
.setCharacteristicUuid(characteristic)
.setBytes(data)
.setWriteOperationAckStrategy(booleanObservable -> {
return Observable.zip(
Observable.timer(delayInMillis, MILLISECONDS).repeat()
,booleanObservable, (callback0, aBoolean) -> aBoolean);
})
.build()
Your approach would delay next (sub)writes if Observable.timer().repeat() would emit after the booleanObservable. Unfortunately that would work only for the second (sub)write as after that .repeat() would start to emit very quickly as it does not resubscribe to the upstream Observable. From .repeat() Javadoc:
Returns an Observable that repeats the sequence of items emitted by the source ObservableSource indefinitely.
If you would use Observable.timer(delayInMillis, MILLISECONDS).repeatWhen(completions -> completions) or Observable.interval(delayInMillis, MILLISECONDS) then these would make writes happen not more frequent than delayInMillis, MILLISECONDS apart.
If you would like to give the peripheral delayInMillis, MILLISECONDS time before issuing next write then there seems to be a simpler approach:
rxBleConnection.createNewLongWriteBuilder()
.setCharacteristicUuid(characteristic)
.setBytes(data)
.setWriteOperationAckStrategy(booleanObservable -> booleanObservable.delay(delayInMillis, MILLISECONDS))
.build()
Related
I need to poll endpoint every second, currently I do it with
Observable.interval(0, 1, TimeUnit.SECONDS, ioScheduler)
.switchMap { return pollWithRetrofit() }
It works fine except the situation when the calls start taking more than 1 second to process, so the retrofit subscription is cancelled by swithMap before I get any response. It can happen multiple times in a row, effectively leaving the client without any response from the poll calls for long duration. In this case I would like to not cancel the retrofit call until I get a response from the next call.
I know that switchMap cancels the previous subscription when the base subscription produces onNext call, currently it happens every second by the Observable.interval, so my idea is to cancel previous call only when the retrofit calls it's onNext, i.e moving the switching one step forward the reactive chain.
How do I do that? Or is there some other solution?
You could use onBackpressureDrop and flatMap with maxConcurrency of 1 to make sure a longer call is still allowed to succeed:
Flowable.interval(0, 1, TimeUnit.SECONDS, ioScheduler)
.onBackpressureDrop()
.flatMap(v -> pollWithRetrofit(), 1);
fwiw I'm using code like following for doing something similar...am not fully convinced that this is cleanest approach either (though has been working successfully for a while now)
someRetrofitInterface.apiCall()
.repeatWhen { completed -> completed.delay(30, TimeUnit.SECONDS) }
.retry(3)
(from https://github.com/joreilly/galway-bus-android/blob/master/base/src/main/java/com/surrus/galwaybus/domain/interactor/GetNearestBusStopsUseCase.kt)
Hi i am trying to poll a request using rxjava repeatUntil but getting some error on it
below is my code
accountDelegator.signUpReady(signUpRequest)
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.repeatUntil { response ->
if (response.isSuccesfull){
return onComplete()
}
}
it says it requires a booleanSupplier not a unit?
i am simply trying to repeat the request above until i get a response.isSuccessful and then returning onComplete() as in the rxjava docs it states that to exit a repeatUntil observer, you have to call onComplete
repeatUntil does not provide any items to its BooleanSupplier function which function is expected to indicate whether or not to repeat the upstream. To "exit" it, you have to return true from the function as you can't call onComplete on anything there (nor does it make sense, you likely misinterpreted the documentation).
You could instead use filter and take which can be used to stop an otherwise repeating sequence:
accountDelegator.signUpReady(signUpRequest)
.subscribeOn(Schedulers.io())
.repeat(/* 100 */)
.filter { response -> response.isSuccessful }
.take(1)
.observeOn(AndroidSchedulers.mainThread());
You'd also want to limit the number of retries and/or delay the repetition by some time (so that your code doesn't spam the server just to not succeed) via repeatWhen.
Edit
To detail the last sentence about delayed retries, here is a way of doing that:
.repeatWhen { completion -> completion.delay(1, TimeUnit.SECONDS) }
instead of repeat(100). When the upstream completes, an object is signalled through completion which is then delayed by 1 seconds. After that, the other side in repeatWhen receives the object which triggers a resubscription to the upstream.
I want to achieve the following with RxJava and as I may not have enough knowledge in this area would like to have some help :)
I need to create a PublishSubject which would emit events with the following sequence:
Emit 1, 2, 3
Buffer 4 in subscribe's completion if a certain condition is not satisfied (may be a network connection for example or some other condition).
For 5, 6 ... buffer after 4 if the condition is not satisfied yet.
Repeat to emit 4 after some time when the condition is satisfied.
If trying to emit 5,6 and the condition is satisfied, then instead of buffering 5, 6 ... after 4, just emit 4 and then 5, 6, 7 , 8 ...
The last 2 points are necessary because the sequence of emitting is really important, which makes difficulties for me to achieve to this.
I hope I could describe what I want to achieve :)
Findings: After asking this question I've done some findings and achieved the following:
private Observable observable = publishSubject
.observeOn(Schedulers.io())
.map(Manager::callNew)
.doOnError(throwable -> Logger.e(throwable, "Error occurred"))
.retryWhen(throwableObservable -> throwableObservable
.zipWith(Observable.range(1, 10), (n, i) -> i)
.flatMap(retryCount -> {
long retrySeconds = (long) Math.pow(2, retryCount);
Logger.d("The call has been failed retrying in %s seconds. Retry count %s", retrySeconds, retryCount);
return Observable.timer(retrySeconds, TimeUnit.SECONDS)
.doOnNext(aLong -> {
C24Logger.d("Timer was completed. %s", aLong);
})
.doOnComplete(() -> Logger.d("Timer was completed."));
}));
The problem is here with PublishSubject. Because it already has emitted all the items, it emits only new ones for retryWhen. If I use ReplaySubject them it emits also the old completed items too for the new retryWhen re-subscribe, which I do not need anymore.
Is there a way to use the ReplaySubject to remove the completed items from the buffer?
You want to be able to turn buffering on and off, depending upon an external condition. Perhaps the simplest way to do it is use the buffer() operator to continually buffer items based on the condition.
(I have removed stuff from the observer chain)
private Observable observable = publishSubject
.publish( obs -> obs.buffer( obs.filter( v -> externalCondition( v ) ) ) )
.flatMapIterable( bufferedList -> bufferedList )
.subscribe( ... );
The publish() operator allows multiple observer chains to subscribe to the incoming observer chain. The buffer() operator monitors the observable that emits a value only when the external condition is true.
When the external condition is true, buffer() will emit a series of lists with only a single element. When the condition goes false, buffer() starts buffering up the results, and when the condition goes true again, all the buffered items are emitted as a list. The flatMapIterable() step will take each item out of the buffer and emit it separately.
I have to schedule same observable repeatedly for N number of times with M seconds delay between each observable:
O1____1sec____O2____1sec____O3____1sec____O4(completed)
Notice no delay between start and ending observable,
Observable<Precious> result = <~> // hidden for brevity (it's just a long time consuming observable that can take suppose up to 10 seconds or more)
Observable.timer(M,TimeUnit.SECONDS).compose(x -> result).take(N).subscribe();
Problem Here is result observable that is doing expensive network calls, will it timout itself after timer expires , or we have to tell it to do so , if so how?
You can use the combination of concatMap to concat the observables, and Delay to delay the emission of every one
/**
* Another elegant solution it would be to create an observable with the list of items, and then use
* concatMap to pass all items from the first observable to the second, then this second observable
* can be created used delay operator afterwards.
*/
#Test
public void delayObservablesWithConcatMap() {
Observable.from(Arrays.asList(Observable.just(1), Observable.just(2),Observable.just(3)))
.concatMap(s -> s.delay(100, TimeUnit.MILLISECONDS))
.subscribe(n -> System.out.println(n + " just emitted..."),
e -> {
},
() -> System.out.println("Everybody emitt!"));
new TestSubscriber().awaitTerminalEvent(1000, TimeUnit.MILLISECONDS);
}
You can see more examples of delay here https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/utils/ObservableDelay.java
To space out items from obsevable source so that they're emitted every N seconds, use the following pattern:
source.zipWith(Observable.interval(1, TimeUnit.SECONDS), (src, dummy) -> src)
Caveat here is that if your source observable takes more time than the interval, then the items get queued up.
Now that I've re-read your question and clarifications, I think what you need is this:
Observable.interval(1, TimeUnit.SECONDS)
.switchMap(dummy -> result)
This will unsubscribe and resubscribe to the result observable every 1 second. It will only workif your result observable cancels network calls on unsubscription.
I am playing around with RxAndroid. I have a List of Observables all of which are api requests (using Retrofit). I want to fire one of them every x seconds or milliseconds but then zip the responses together. I seems that once I subscribe to Observable.zip(requests, someFunction) all of them are fired off at once. Any tips?
Thanks!
EDIT: looks like adding delaySubscription to each request maybe the answer
You are looking for either delay() or delaySubscription().
delay() will delay the result of the Observable being published to the subscriber.
delaySubscription() will delay subscription to the Observable.
Observable.zip(someObservable.delaySubscription(100, TimeUnit.MILLISECONDS),
someOtherObservable.delaySubscription(200, TimeUnit.MILLISECONDS),
someThirdObservable.delaySubscription(300, TimeUnit.MILLISECONDS),
new Func3<Object, Object, Object, Void>() {
...
}).subscribe();
Also, it's posible to achieve a periodical sending effect by using the interval() operator.
Let's see a simple example. Imagine you have an array, numbers, whose values have to emitted each x time. You could create an Observable that emits them:
Observable<Integer> values = Observable.from(numbers);
And now, another Observable that emits each (for instance) 30 milliseconds:
Observable<Long> interval = Observable.interval(30, TimeUnit.MILLISECONDS);
So, through the zip() operator you could combine both to achieve the periodical emission of the values in your number array:
Observable.zip(values, interval, (arrayElement, aLong) -> arrayElement)
.subscribe(arrayElement -> doSomething(arrayElement));
I used it to get an animation effect for a progress indicator. I wrote a complete example project you can check in github.