15
15
*/
16
16
package rx ;
17
17
18
+ import static org .mockito .Matchers .*;
19
+ import static org .mockito .Mockito .*;
20
+
18
21
import java .util .Date ;
19
22
import java .util .concurrent .TimeUnit ;
23
+ import java .util .concurrent .atomic .AtomicBoolean ;
24
+
25
+ import org .junit .Test ;
26
+ import org .mockito .InOrder ;
27
+ import org .mockito .Mockito ;
20
28
29
+ import rx .concurrency .TestScheduler ;
21
30
import rx .subscriptions .Subscriptions ;
22
31
import rx .util .functions .Action0 ;
23
32
import rx .util .functions .Func0 ;
@@ -71,6 +80,56 @@ public abstract class Scheduler {
71
80
*/
72
81
public abstract <T > Subscription schedule (T state , Func2 <Scheduler , T , Subscription > action , long delayTime , TimeUnit unit );
73
82
83
+ /**
84
+ * Schedules a cancelable action to be executed periodically.
85
+ * This default implementation schedules recursively and waits for actions to complete (instead of potentially executing
86
+ * long-running actions concurrently). Each scheduler that can do periodic scheduling in a better way should override this.
87
+ *
88
+ * @param state
89
+ * State to pass into the action.
90
+ * @param action
91
+ * The action to execute periodically.
92
+ * @param initialDelay
93
+ * Time to wait before executing the action for the first time.
94
+ * @param period
95
+ * The time interval to wait each time in between executing the action.
96
+ * @param unit
97
+ * The time unit the interval above is given in.
98
+ * @return A subscription to be able to unsubscribe from action.
99
+ */
100
+ public <T > Subscription schedulePeriodically (T state , final Func2 <Scheduler , T , Subscription > action , long initialDelay , long period , TimeUnit unit ) {
101
+ final long periodInNanos = unit .toNanos (period );
102
+ final AtomicBoolean complete = new AtomicBoolean ();
103
+
104
+ final Func2 <Scheduler , T , Subscription > recursiveAction = new Func2 <Scheduler , T , Subscription >() {
105
+ @ Override
106
+ public Subscription call (Scheduler scheduler , T state0 ) {
107
+ if (!complete .get ()) {
108
+ long startedAt = now ();
109
+ final Subscription sub1 = action .call (scheduler , state0 );
110
+ long timeTakenByActionInNanos = TimeUnit .MILLISECONDS .toNanos (now () - startedAt );
111
+ final Subscription sub2 = schedule (state0 , this , periodInNanos - timeTakenByActionInNanos , TimeUnit .NANOSECONDS );
112
+ return Subscriptions .create (new Action0 () {
113
+ @ Override
114
+ public void call () {
115
+ sub1 .unsubscribe ();
116
+ sub2 .unsubscribe ();
117
+ }
118
+ });
119
+ }
120
+ return Subscriptions .empty ();
121
+ }
122
+ };
123
+ final Subscription sub = schedule (state , recursiveAction , initialDelay , unit );
124
+ return Subscriptions .create (new Action0 () {
125
+ @ Override
126
+ public void call () {
127
+ complete .set (true );
128
+ sub .unsubscribe ();
129
+ }
130
+ });
131
+ }
132
+
74
133
/**
75
134
* Schedules a cancelable action to be executed at dueTime.
76
135
*
@@ -103,7 +162,7 @@ public Subscription schedule(final Func1<Scheduler, Subscription> action) {
103
162
return schedule (null , new Func2 <Scheduler , Void , Subscription >() {
104
163
105
164
@ Override
106
- public Subscription call (Scheduler scheduler , Void t2 ) {
165
+ public Subscription call (Scheduler scheduler , @ SuppressWarnings ( "unused" ) Void state ) {
107
166
return action .call (scheduler );
108
167
}
109
168
});
@@ -120,7 +179,7 @@ public Subscription schedule(final Func0<Subscription> action) {
120
179
return schedule (null , new Func2 <Scheduler , Void , Subscription >() {
121
180
122
181
@ Override
123
- public Subscription call (Scheduler scheduler , Void t2 ) {
182
+ public Subscription call (@ SuppressWarnings ( "unused" ) Scheduler scheduler , @ SuppressWarnings ( "unused" ) Void state ) {
124
183
return action .call ();
125
184
}
126
185
});
@@ -137,7 +196,7 @@ public Subscription schedule(final Action0 action) {
137
196
return schedule (null , new Func2 <Scheduler , Void , Subscription >() {
138
197
139
198
@ Override
140
- public Subscription call (Scheduler scheduler , Void t2 ) {
199
+ public Subscription call (@ SuppressWarnings ( "unused" ) Scheduler scheduler , @ SuppressWarnings ( "unused" ) Void state ) {
141
200
action .call ();
142
201
return Subscriptions .empty ();
143
202
}
@@ -159,7 +218,7 @@ public Subscription schedule(final Func1<Scheduler, Subscription> action, long d
159
218
return schedule (null , new Func2 <Scheduler , Void , Subscription >() {
160
219
161
220
@ Override
162
- public Subscription call (Scheduler scheduler , Void t2 ) {
221
+ public Subscription call (Scheduler scheduler , @ SuppressWarnings ( "unused" ) Void state ) {
163
222
return action .call (scheduler );
164
223
}
165
224
}, delayTime , unit );
@@ -176,7 +235,7 @@ public Subscription schedule(final Action0 action, long delayTime, TimeUnit unit
176
235
return schedule (null , new Func2 <Scheduler , Void , Subscription >() {
177
236
178
237
@ Override
179
- public Subscription call (Scheduler scheduler , Void t2 ) {
238
+ public Subscription call (@ SuppressWarnings ( "unused" ) Scheduler scheduler , @ SuppressWarnings ( "unused" ) Void state ) {
180
239
action .call ();
181
240
return Subscriptions .empty ();
182
241
}
@@ -194,17 +253,123 @@ public Subscription schedule(final Func0<Subscription> action, long delayTime, T
194
253
return schedule (null , new Func2 <Scheduler , Void , Subscription >() {
195
254
196
255
@ Override
197
- public Subscription call (Scheduler scheduler , Void t2 ) {
256
+ public Subscription call (@ SuppressWarnings ( "unused" ) Scheduler scheduler , @ SuppressWarnings ( "unused" ) Void state ) {
198
257
return action .call ();
199
258
}
200
259
}, delayTime , unit );
201
260
}
202
261
262
+ /**
263
+ * Schedules a cancelable action to be executed periodically.
264
+ *
265
+ * @param action
266
+ * The action to execute periodically.
267
+ * @param initialDelay
268
+ * Time to wait before executing the action for the first time.
269
+ * @param period
270
+ * The time interval to wait each time in between executing the action.
271
+ * @param unit
272
+ * The time unit the interval above is given in.
273
+ * @return A subscription to be able to unsubscribe from action.
274
+ */
275
+ public Subscription schedulePeriodically (final Func1 <Scheduler , Subscription > action , long initialDelay , long period , TimeUnit unit ) {
276
+ return schedulePeriodically (null , new Func2 <Scheduler , Void , Subscription >() {
277
+ @ Override
278
+ public Subscription call (Scheduler scheduler , @ SuppressWarnings ("unused" ) Void state ) {
279
+ return action .call (scheduler );
280
+ }
281
+ }, initialDelay , period , unit );
282
+ }
283
+
284
+ /**
285
+ * Schedules a cancelable action to be executed periodically.
286
+ *
287
+ * @param action
288
+ * The action to execute periodically.
289
+ * @param initialDelay
290
+ * Time to wait before executing the action for the first time.
291
+ * @param period
292
+ * The time interval to wait each time in between executing the action.
293
+ * @param unit
294
+ * The time unit the interval above is given in.
295
+ * @return A subscription to be able to unsubscribe from action.
296
+ */
297
+ public Subscription schedulePeriodically (final Func0 <Subscription > action , long initialDelay , long period , TimeUnit unit ) {
298
+ return schedulePeriodically (null , new Func2 <Scheduler , Void , Subscription >() {
299
+ @ Override
300
+ public Subscription call (@ SuppressWarnings ("unused" ) Scheduler scheduler , @ SuppressWarnings ("unused" ) Void state ) {
301
+ return action .call ();
302
+ }
303
+ }, initialDelay , period , unit );
304
+ }
305
+
306
+ /**
307
+ * Schedules an action to be executed periodically.
308
+ *
309
+ * @param action
310
+ * The action to execute periodically.
311
+ * @param initialDelay
312
+ * Time to wait before executing the action for the first time.
313
+ * @param period
314
+ * The time interval to wait each time in between executing the action.
315
+ * @param unit
316
+ * The time unit the interval above is given in.
317
+ * @return A subscription to be able to unsubscribe from action.
318
+ */
319
+ public Subscription schedulePeriodically (final Action0 action , long initialDelay , long period , TimeUnit unit ) {
320
+ return schedulePeriodically (null , new Func2 <Scheduler , Void , Subscription >() {
321
+ @ Override
322
+ public Subscription call (@ SuppressWarnings ("unused" ) Scheduler scheduler , @ SuppressWarnings ("unused" ) Void state ) {
323
+ action .call ();
324
+ return Subscriptions .empty ();
325
+ }
326
+ }, initialDelay , period , unit );
327
+ }
328
+
203
329
/**
204
330
* Returns the scheduler's notion of current absolute time in milliseconds.
205
331
*/
206
332
public long now () {
207
333
return System .currentTimeMillis ();
208
334
}
209
335
336
+ public static class UnitTest {
337
+ @ SuppressWarnings ("unchecked" ) // mocking is unchecked, unfortunately
338
+ @ Test
339
+ public void testPeriodicScheduling () {
340
+ final Func1 <Long , Void > calledOp = mock (Func1 .class );
341
+
342
+ final TestScheduler scheduler = new TestScheduler ();
343
+ Subscription subscription = scheduler .schedulePeriodically (new Action0 () {
344
+ @ Override public void call () {
345
+ System .out .println (scheduler .now ());
346
+ calledOp .call (scheduler .now ());
347
+ }
348
+ }, 1 , 2 , TimeUnit .SECONDS );
349
+
350
+ verify (calledOp , never ()).call (anyLong ());
351
+
352
+ InOrder inOrder = Mockito .inOrder (calledOp );
353
+
354
+ scheduler .advanceTimeBy (999L , TimeUnit .MILLISECONDS );
355
+ inOrder .verify (calledOp , never ()).call (anyLong ());
356
+
357
+ scheduler .advanceTimeBy (1L , TimeUnit .MILLISECONDS );
358
+ inOrder .verify (calledOp , times (1 )).call (1000L );
359
+
360
+ scheduler .advanceTimeBy (1999L , TimeUnit .MILLISECONDS );
361
+ inOrder .verify (calledOp , never ()).call (3000L );
362
+
363
+ scheduler .advanceTimeBy (1L , TimeUnit .MILLISECONDS );
364
+ inOrder .verify (calledOp , times (1 )).call (3000L );
365
+
366
+ scheduler .advanceTimeBy (5L , TimeUnit .SECONDS );
367
+ inOrder .verify (calledOp , times (1 )).call (5000L );
368
+ inOrder .verify (calledOp , times (1 )).call (7000L );
369
+
370
+ subscription .unsubscribe ();
371
+ scheduler .advanceTimeBy (11L , TimeUnit .SECONDS );
372
+ inOrder .verify (calledOp , never ()).call (anyLong ());
373
+ }
374
+ }
210
375
}
0 commit comments