22
22
import rx .Subscriber ;
23
23
import rx .observables .GroupedObservable ;
24
24
import rx .subjects .PublishSubject ;
25
- import rx .subscriptions .CompositeSubscription ;
26
- import rx .subscriptions .Subscriptions ;
27
- import rx .util .functions .Action0 ;
28
25
import rx .util .functions .Action1 ;
29
26
30
27
/**
31
- * Asynchronously subscribes and unsubscribes Observers on the specified Scheduler.
28
+ * Subscribes and unsubscribes Observers on the specified Scheduler.
32
29
* <p>
30
+ * Will occur asynchronously except when subscribing to `GroupedObservable`, `PublishSubject` and possibly other "hot" Observables
31
+ * in which case it will subscribe synchronously and buffer/block onNext calls until the subscribe has occurred.
32
+ * <p>
33
+ * See https://github.com/Netflix/RxJava/issues/844 for more information on the "time gap" issue that the synchronous
34
+ * subscribe is solving.
35
+ *
33
36
* <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/subscribeOn.png">
34
37
*/
35
38
public class OperatorSubscribeOn <T > implements Operator <T , Observable <T >> {
36
39
37
40
private final Scheduler scheduler ;
38
- /**
41
+ /**
39
42
* Indicate that events fired between the original subscription time and
40
43
* the actual subscription time should not get lost.
41
44
*/
42
45
private final boolean dontLoseEvents ;
43
46
/** The buffer size to avoid flooding. Negative value indicates an unbounded buffer. */
44
47
private final int bufferSize ;
48
+
45
49
public OperatorSubscribeOn (Scheduler scheduler , boolean dontLoseEvents ) {
46
50
this (scheduler , dontLoseEvents , -1 );
47
51
}
52
+
48
53
/**
49
54
* Construct a SubscribeOn operator.
50
- * @param scheduler the target scheduler
51
- * @param dontLoseEvents indicate that events should be buffered until the actual subscription happens
52
- * @param bufferSize if dontLoseEvents == true, this indicates the buffer size. Filling the buffer will
53
- * block the source. -1 indicates an unbounded buffer
55
+ *
56
+ * @param scheduler
57
+ * the target scheduler
58
+ * @param dontLoseEvents
59
+ * indicate that events should be buffered until the actual subscription happens
60
+ * @param bufferSize
61
+ * if dontLoseEvents == true, this indicates the buffer size. Filling the buffer will
62
+ * block the source. -1 indicates an unbounded buffer
54
63
*/
55
64
public OperatorSubscribeOn (Scheduler scheduler , boolean dontLoseEvents , int bufferSize ) {
56
65
this .scheduler = scheduler ;
@@ -71,78 +80,61 @@ public void onCompleted() {
71
80
public void onError (Throwable e ) {
72
81
subscriber .onError (e );
73
82
}
83
+
74
84
boolean checkNeedBuffer (Observable <?> o ) {
75
- return dontLoseEvents || ((o instanceof GroupedObservable <?, ?>)
85
+ /*
86
+ * Included are some Observable types known to be "hot" and thus needing
87
+ * buffering when subscribing across thread boundaries otherwise
88
+ * we can lose data.
89
+ *
90
+ * See https://github.com/Netflix/RxJava/issues/844 for more information.
91
+ */
92
+ return dontLoseEvents
93
+ || ((o instanceof GroupedObservable <?, ?>)
76
94
|| (o instanceof PublishSubject <?>)
77
95
// || (o instanceof BehaviorSubject<?, ?>)
78
96
);
79
97
}
98
+
80
99
@ Override
81
100
public void onNext (final Observable <T > o ) {
82
101
if (checkNeedBuffer (o )) {
83
- final CompositeSubscription cs = new CompositeSubscription ();
84
- subscriber .add (cs );
85
- final BufferUntilSubscriber <T > bus = new BufferUntilSubscriber <T >(bufferSize , subscriber , new CompositeSubscription ());
102
+ // use buffering (possibly blocking) for a possibly synchronous subscribe
103
+ final BufferUntilSubscriber <T > bus = new BufferUntilSubscriber <T >(bufferSize , subscriber );
86
104
o .subscribe (bus );
87
- scheduler .schedule (new Action1 <Inner >() {
105
+ subscriber . add ( scheduler .schedule (new Action1 <Inner >() {
88
106
@ Override
89
107
public void call (final Inner inner ) {
90
- cs .add (Subscriptions .create (new Action0 () {
91
- @ Override
92
- public void call () {
93
- inner .schedule (new Action1 <Inner >() {
94
- @ Override
95
- public void call (final Inner inner ) {
96
- bus .unsubscribe ();
97
- }
98
- });
99
- }
100
- }));
101
108
bus .enterPassthroughMode ();
102
109
}
103
- });
110
+ })) ;
104
111
return ;
105
- }
106
- scheduler .schedule (new Action1 <Inner >() {
107
-
108
- @ Override
109
- public void call (final Inner inner ) {
110
- final CompositeSubscription cs = new CompositeSubscription ();
111
- subscriber .add (Subscriptions .create (new Action0 () {
112
-
113
- @ Override
114
- public void call () {
115
- inner .schedule (new Action1 <Inner >() {
116
-
117
- @ Override
118
- public void call (final Inner inner ) {
119
- cs .unsubscribe ();
120
- }
121
-
122
- });
123
- }
112
+ } else {
113
+ // no buffering (async subscribe)
114
+ scheduler .schedule (new Action1 <Inner >() {
124
115
125
- }));
126
- cs . add ( subscriber );
127
- o .subscribe (new Subscriber <T >(cs ) {
116
+ @ Override
117
+ public void call ( final Inner inner ) {
118
+ o .subscribe (new Subscriber <T >(subscriber ) {
128
119
129
- @ Override
130
- public void onCompleted () {
131
- subscriber .onCompleted ();
132
- }
120
+ @ Override
121
+ public void onCompleted () {
122
+ subscriber .onCompleted ();
123
+ }
133
124
134
- @ Override
135
- public void onError (Throwable e ) {
136
- subscriber .onError (e );
137
- }
125
+ @ Override
126
+ public void onError (Throwable e ) {
127
+ subscriber .onError (e );
128
+ }
138
129
139
- @ Override
140
- public void onNext (T t ) {
141
- subscriber .onNext (t );
142
- }
143
- });
144
- }
145
- });
130
+ @ Override
131
+ public void onNext (T t ) {
132
+ subscriber .onNext (t );
133
+ }
134
+ });
135
+ }
136
+ });
137
+ }
146
138
}
147
139
148
140
};
0 commit comments