15
15
*/
16
16
package rx .operators ;
17
17
18
- import static org .mockito .Matchers .*;
19
- import static org .mockito .Mockito .*;
18
+ import static org .mockito .Matchers .any ;
19
+ import static org .mockito .Matchers .anyString ;
20
+ import static org .mockito .Mockito .inOrder ;
21
+ import static org .mockito .Mockito .mock ;
22
+ import static org .mockito .Mockito .never ;
23
+ import static org .mockito .Mockito .times ;
24
+ import static org .mockito .Mockito .verify ;
20
25
21
26
import java .util .concurrent .TimeUnit ;
22
- import java .util .concurrent .atomic .AtomicReference ;
23
27
24
28
import org .junit .Before ;
25
29
import org .junit .Test ;
30
34
import rx .Observer ;
31
35
import rx .Subscription ;
32
36
import rx .concurrency .TestScheduler ;
37
+ import rx .subscriptions .MultipleAssignmentSubscription ;
33
38
import rx .subscriptions .Subscriptions ;
34
39
import rx .util .functions .Action0 ;
35
40
import rx .util .functions .Func1 ;
36
41
37
42
/**
38
- * Transforms an Observable that emits Observables into a single Observable that emits the items
39
- * emitted by the most recently published of those Observables.
43
+ * Transforms an Observable that emits Observables into a single Observable that
44
+ * emits the items emitted by the most recently published of those Observables.
40
45
* <p>
41
- * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/switchDo.png">
46
+ * <img width="640" src=
47
+ * "https://github.com/Netflix/RxJava/wiki/images/rx-operators/switchDo.png">
42
48
*/
43
49
public final class OperationSwitch {
44
50
45
51
/**
46
- * This function transforms an {@link Observable} sequence of {@link Observable} sequences into a single {@link Observable} sequence which produces values from the most recently published
47
- * {@link Observable}.
52
+ * This function transforms an {@link Observable} sequence of
53
+ * {@link Observable} sequences into a single {@link Observable} sequence
54
+ * which produces values from the most recently published {@link Observable}
55
+ * .
48
56
*
49
57
* @param sequences
50
- * The {@link Observable} sequence consisting of {@link Observable} sequences.
58
+ * The {@link Observable} sequence consisting of
59
+ * {@link Observable} sequences.
51
60
* @return A {@link Func1} which does this transformation.
52
61
*/
53
62
public static <T > OnSubscribeFunc <T > switchDo (final Observable <? extends Observable <? extends T >> sequences ) {
@@ -77,61 +86,99 @@ public Subscription onSubscribe(Observer<? super T> observer) {
77
86
78
87
private static class SwitchObserver <T > implements Observer <Observable <? extends T >> {
79
88
80
- private final Observer <? super T > observer ;
81
- private final SafeObservableSubscription parent ;
82
- private final AtomicReference <Subscription > subsequence = new AtomicReference <Subscription >();
89
+ private final Object gate ;
90
+ private final Observer <? super T > observer ;
91
+ private final SafeObservableSubscription parent ;
92
+ private final MultipleAssignmentSubscription innerSubscription ;
93
+ private long latest ;
94
+ private boolean stopped ;
95
+ private boolean hasLatest ;
83
96
84
97
public SwitchObserver (Observer <? super T > observer , SafeObservableSubscription parent ) {
85
98
this .observer = observer ;
86
99
this .parent = parent ;
87
- }
88
-
89
- @ Override
90
- public void onCompleted () {
91
- unsubscribeFromSubSequence ();
92
- observer .onCompleted ();
93
- }
94
-
95
- @ Override
96
- public void onError (Throwable e ) {
97
- unsubscribeFromSubSequence ();
98
- observer .onError (e );
100
+ this .gate = new Object ();
101
+ this .innerSubscription = new MultipleAssignmentSubscription ();
99
102
}
100
103
101
104
@ Override
102
105
public void onNext (Observable <? extends T > args ) {
103
- unsubscribeFromSubSequence ();
106
+ final long id ;
107
+ synchronized (gate ) {
108
+ id = ++latest ;
109
+ hasLatest = true ;
110
+ }
104
111
105
- subsequence .set (args .subscribe (new Observer <T >() {
112
+ final SafeObservableSubscription sub ;
113
+ sub = new SafeObservableSubscription ();
114
+ sub .wrap (args .subscribe (new Observer <T >() {
106
115
@ Override
107
- public void onCompleted () {
108
- // Do nothing.
116
+ public void onNext (T args ) {
117
+ synchronized (gate ) {
118
+ if (latest == id ) {
119
+ observer .onNext (args );
120
+ }
121
+ }
109
122
}
110
123
111
124
@ Override
112
125
public void onError (Throwable e ) {
113
- parent .unsubscribe ();
114
- observer .onError (e );
126
+ synchronized (gate ) {
127
+ sub .unsubscribe ();
128
+ if (latest == id ) {
129
+ observer .onError (e );
130
+ parent .unsubscribe ();
131
+ }
132
+ }
115
133
}
116
134
117
135
@ Override
118
- public void onNext (T args ) {
119
- observer .onNext (args );
136
+ public void onCompleted () {
137
+ synchronized (gate ) {
138
+ sub .unsubscribe ();
139
+ if (latest == id ) {
140
+ hasLatest = false ;
141
+ }
142
+
143
+ if (stopped ) {
144
+ observer .onCompleted ();
145
+ parent .unsubscribe ();
146
+ }
147
+
148
+ }
120
149
}
150
+
121
151
}));
152
+
153
+ innerSubscription .setSubscription (sub );
154
+ }
155
+
156
+ @ Override
157
+ public void onError (Throwable e ) {
158
+ synchronized (gate ) {
159
+ observer .onError (e );
160
+ }
161
+
162
+ parent .unsubscribe ();
122
163
}
123
164
124
- private void unsubscribeFromSubSequence () {
125
- Subscription previousSubscription = subsequence .get ();
126
- if (previousSubscription != null ) {
127
- previousSubscription .unsubscribe ();
165
+ @ Override
166
+ public void onCompleted () {
167
+ synchronized (gate ) {
168
+ innerSubscription .unsubscribe ();
169
+ stopped = true ;
170
+ if (!hasLatest ) {
171
+ observer .onCompleted ();
172
+ parent .unsubscribe ();
173
+ }
128
174
}
129
175
}
176
+
130
177
}
131
178
132
179
public static class UnitTest {
133
180
134
- private TestScheduler scheduler ;
181
+ private TestScheduler scheduler ;
135
182
private Observer <String > observer ;
136
183
137
184
@ Before
@@ -141,6 +188,83 @@ public void before() {
141
188
observer = mock (Observer .class );
142
189
}
143
190
191
+ @ Test
192
+ public void testSwitchWhenOuterCompleteBeforeInner () {
193
+ Observable <Observable <String >> source = Observable .create (new OnSubscribeFunc <Observable <String >>() {
194
+ @ Override
195
+ public Subscription onSubscribe (Observer <? super Observable <String >> observer ) {
196
+ publishNext (observer , 50 , Observable .create (new OnSubscribeFunc <String >() {
197
+ @ Override
198
+ public Subscription onSubscribe (Observer <? super String > observer ) {
199
+ publishNext (observer , 70 , "one" );
200
+ publishNext (observer , 100 , "two" );
201
+ publishCompleted (observer , 200 );
202
+ return Subscriptions .empty ();
203
+ }
204
+ }));
205
+ publishCompleted (observer , 60 );
206
+
207
+ return Subscriptions .empty ();
208
+ }
209
+ });
210
+
211
+ Observable <String > sampled = Observable .create (OperationSwitch .switchDo (source ));
212
+ sampled .subscribe (observer );
213
+
214
+ InOrder inOrder = inOrder (observer );
215
+
216
+ scheduler .advanceTimeTo (350 , TimeUnit .MILLISECONDS );
217
+ inOrder .verify (observer , times (2 )).onNext (anyString ());
218
+ inOrder .verify (observer , times (1 )).onCompleted ();
219
+ }
220
+
221
+ @ Test
222
+ public void testSwitchWhenInnerCompleteBeforeOuter () {
223
+ Observable <Observable <String >> source = Observable .create (new OnSubscribeFunc <Observable <String >>() {
224
+ @ Override
225
+ public Subscription onSubscribe (Observer <? super Observable <String >> observer ) {
226
+ publishNext (observer , 10 , Observable .create (new OnSubscribeFunc <String >() {
227
+ @ Override
228
+ public Subscription onSubscribe (Observer <? super String > observer ) {
229
+ publishNext (observer , 0 , "one" );
230
+ publishNext (observer , 10 , "two" );
231
+ publishCompleted (observer , 20 );
232
+ return Subscriptions .empty ();
233
+ }
234
+ }));
235
+
236
+ publishNext (observer , 100 , Observable .create (new OnSubscribeFunc <String >() {
237
+ @ Override
238
+ public Subscription onSubscribe (Observer <? super String > observer ) {
239
+ publishNext (observer , 0 , "three" );
240
+ publishNext (observer , 10 , "four" );
241
+ publishCompleted (observer , 20 );
242
+ return Subscriptions .empty ();
243
+ }
244
+ }));
245
+ publishCompleted (observer , 200 );
246
+
247
+ return Subscriptions .empty ();
248
+ }
249
+ });
250
+
251
+ Observable <String > sampled = Observable .create (OperationSwitch .switchDo (source ));
252
+ sampled .subscribe (observer );
253
+
254
+ InOrder inOrder = inOrder (observer );
255
+
256
+ scheduler .advanceTimeTo (150 , TimeUnit .MILLISECONDS );
257
+ inOrder .verify (observer , never ()).onCompleted ();
258
+ inOrder .verify (observer , times (1 )).onNext ("one" );
259
+ inOrder .verify (observer , times (1 )).onNext ("two" );
260
+ inOrder .verify (observer , times (1 )).onNext ("three" );
261
+ inOrder .verify (observer , times (1 )).onNext ("four" );
262
+
263
+ scheduler .advanceTimeTo (250 , TimeUnit .MILLISECONDS );
264
+ inOrder .verify (observer , never ()).onNext (anyString ());
265
+ inOrder .verify (observer , times (1 )).onCompleted ();
266
+ }
267
+
144
268
@ Test
145
269
public void testSwitchWithComplete () {
146
270
Observable <Observable <String >> source = Observable .create (new OnSubscribeFunc <Observable <String >>() {
@@ -149,7 +273,7 @@ public Subscription onSubscribe(Observer<? super Observable<String>> observer) {
149
273
publishNext (observer , 50 , Observable .create (new OnSubscribeFunc <String >() {
150
274
@ Override
151
275
public Subscription onSubscribe (Observer <? super String > observer ) {
152
- publishNext (observer , 50 , "one" );
276
+ publishNext (observer , 60 , "one" );
153
277
publishNext (observer , 100 , "two" );
154
278
return Subscriptions .empty ();
155
279
}
@@ -196,8 +320,8 @@ public Subscription onSubscribe(Observer<? super String> observer) {
196
320
verify (observer , never ()).onError (any (Throwable .class ));
197
321
198
322
scheduler .advanceTimeTo (350 , TimeUnit .MILLISECONDS );
199
- inOrder .verify (observer , never ( )).onNext (anyString () );
200
- verify (observer , times ( 1 )).onCompleted ();
323
+ inOrder .verify (observer , times ( 1 )).onNext ("four" );
324
+ verify (observer , never ( )).onCompleted ();
201
325
verify (observer , never ()).onError (any (Throwable .class ));
202
326
}
203
327
0 commit comments