Cela émettra une tique toutes les 5 secondes.
Observable.interval(5, TimeUnit.SECONDS, Schedulers.io())
.subscribe(tick -> Log.d(TAG, "tick = "+tick));
Pour l'arrêter, vous pouvez utiliser
Schedulers.shutdown();
Mais alors tous les planificateurs s'arrêtent et il n'est pas possible de reprendre le ticking plus tard. Comment puis-je arrêter et reprendre l'émission "gracieusement" `?
Voici une solution possible:
class TickHandler {
private AtomicLong lastTick = new AtomicLong(0L);
private Subscription subscription;
void resume() {
System.out.println("resumed");
subscription = Observable.interval(5, TimeUnit.SECONDS, Schedulers.io())
.map(tick -> lastTick.getAndIncrement())
.subscribe(tick -> System.out.println("tick = " + tick));
}
void stop() {
if (subscription != null && !subscription.isUnsubscribed()) {
System.out.println("stopped");
subscription.unsubscribe();
}
}
}
Il y a quelque temps, je recherchais également des solutions de type "minuterie" RX, mais aucune d'entre elles ne répondait à mes attentes. Donc là vous pouvez trouver ma propre solution:
AtomicLong elapsedTime = new AtomicLong();
AtomicBoolean resumed = new AtomicBoolean();
AtomicBoolean stopped = new AtomicBoolean();
public Flowable<Long> startTimer() { //Create and starts timper
resumed.set(true);
stopped.set(false);
return Flowable.interval(1, TimeUnit.SECONDS)
.takeWhile(tick -> !stopped.get())
.filter(tick -> resumed.get())
.map(tick -> elapsedTime.addAndGet(1000));
}
public void pauseTimer() {
resumed.set(false);
}
public void resumeTimer() {
resumed.set(true);
}
public void stopTimer() {
stopped.set(true);
}
public void addToTimer(int seconds) {
elapsedTime.addAndGet(seconds * 1000);
}
val switch = new Java.util.concurrent.atomic.AtomicBoolean(true)
val tick = new Java.util.concurrent.atomic.AtomicLong(0L)
val suspendableObservable =
Observable.
interval(5 seconds).
takeWhile(_ => switch.get()).
repeat.
map(_ => tick.incrementAndGet())
Vous pouvez définir switch
sur false
pour suspendre le ticking et true
pour le reprendre.
Voici une autre façon de faire cela, je pense.
Lorsque vous vérifiez le code source, vous trouvez interval () using class OnSubscribeTimerPeriodically . Le code clé ci-dessous.
@Override
public void call(final Subscriber<? super Long> child) {
final Worker worker = scheduler.createWorker();
child.add(worker);
worker.schedulePeriodically(new Action0() {
long counter;
@Override
public void call() {
try {
child.onNext(counter++);
} catch (Throwable e) {
try {
worker.unsubscribe();
} finally {
Exceptions.throwOrReport(e, child);
}
}
}
}, initialDelay, period, unit);
}
Donc, vous verrez, si vous voulez canaliser la boucle, pourquoi ne pas lancer une nouvelle exception dans onNext () . Exemple de code ci-dessous.
Observable.interval(1000, TimeUnit.MILLISECONDS)
.subscribe(new Action1<Long>() {
@Override
public void call(Long aLong) {
Log.i("abc", "onNext");
if (aLong == 5) throw new NullPointerException();
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
Log.i("abc", "onError");
}
}, new Action0() {
@Override
public void call() {
Log.i("abc", "onCompleted");
}
});
Ensuite, vous verrez ceci:
08-08 11:10:46.008 28146-28181/net.bingyan.test I/abc: onNext
08-08 11:10:47.008 28146-28181/net.bingyan.test I/abc: onNext
08-08 11:10:48.008 28146-28181/net.bingyan.test I/abc: onNext
08-08 11:10:49.008 28146-28181/net.bingyan.test I/abc: onNext
08-08 11:10:50.008 28146-28181/net.bingyan.test I/abc: onNext
08-08 11:10:51.008 28146-28181/net.bingyan.test I/abc: onNext
08-08 11:10:51.018 28146-28181/net.bingyan.test I/abc: onError
Désolé, ceci est dans RxJS au lieu de RxJava, mais le concept sera le même. J'ai adapté cela de learn-rxjs.io et ici c'est sur codepen .
L'idée est que vous démarrez avec deux flux d'événements de clic, startClick$
et stopClick$
. Chaque clic sur le flux stopClick$
est mappé sur un observable vide et les clics sur startClick$
sont mappés sur le flux interval$
. Les deux flux résultants forment merge
- d en un observable d'observables. En d'autres termes, un nouvel observable de l'un des deux types sera émis à partir de merge
à chaque clic. L'observable résultant passera par switchMap
, qui commence à écouter ce nouvel observable et cesse d'écouter ce qu'il écoutait auparavant. Switchmap commencera également à fusionner les valeurs de ce nouvel observable sur son flux existant.
Après le changement, scan
ne voit jamais que la valeur "incrément" émise par interval$
et ne voit aucune valeur lorsque "stop" a été cliqué.
Et jusqu'à ce que le premier clic se produise, startWith
commencera à émettre des valeurs à partir de $interval
, uniquement pour faire avancer les choses:
const start = 0;
const increment = 1;
const delay = 1000;
const stopButton = document.getElementById('stop');
const startButton = document.getElementById('start');
const startClick$ = Rx.Observable.fromEvent(startButton, 'click');
const stopClick$ = Rx.Observable.fromEvent(stopButton, 'click');
const interval$ = Rx.Observable.interval(delay).mapTo(increment);
const setCounter = newValue => document.getElementById("counter").innerHTML = newValue;
setCounter(start);
const timer$ = Rx.Observable
// a "stop" click will emit an empty observable,
// and a "start" click will emit the interval$ observable.
// These two streams are merged into one observable.
.merge(stopClick$.mapTo(Rx.Observable.empty()),
startClick$.mapTo(interval$))
// until the first click occurs, merge will emit nothing, so
// use the interval$ to start the counter in the meantime
.startWith(interval$)
// whenever a new observable starts, stop listening to the previous
// one and start emitting values from the new one
.switchMap(val => val)
// add the increment emitted by the interval$ stream to the accumulator
.scan((acc, curr) => curr + acc, start)
// start the observable and send results to the DIV
.subscribe((x) => setCounter(x));
Et voici le HTML
<html>
<body>
<div id="counter"></div>
<button id="start">
Start
</button>
<button id="stop">
Stop
</button>
</body>
</html>
Vous pouvez utiliser takeWhile et boucle jusqu'à ce que les conditions soient vraies
Observable.interval(1, TimeUnit.SECONDS)
.takeWhile {
Log.i(TAG, " time " + it)
it != 30L
}
.subscribe(object : Observer<Long> {
override fun onComplete() {
Log.i(TAG, "onComplete " + format.format(System.currentTimeMillis()))
}
override fun onSubscribe(d: Disposable) {
Log.i(TAG, "onSubscribe " + format.format(System.currentTimeMillis()))
}
override fun onNext(t: Long) {
Log.i(TAG, "onNext " + format.format(System.currentTimeMillis()))
}
override fun onError(e: Throwable) {
Log.i(TAG, "onError")
e.printStackTrace()
}
});