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