@Override public void call(final Subscriber<? super RecyclerViewChildAttachStateChangeEvent> subscriber) { checkUiThread(); final OnChildAttachStateChangeListener listener = new OnChildAttachStateChangeListener() { @Override public void onChildViewAttachedToWindow(View childView) { if (!subscriber.isUnsubscribed()) { subscriber.onNext(RecyclerViewChildAttachEvent.create(recyclerView, childView)); } } @Override public void onChildViewDetachedFromWindow(View childView) { if (!subscriber.isUnsubscribed()) { subscriber.onNext(RecyclerViewChildDetachEvent.create(recyclerView, childView)); } } }; recyclerView.addOnChildAttachStateChangeListener(listener); subscriber.add( new MainThreadSubscription() { @Override protected void onUnsubscribe() { recyclerView.removeOnChildAttachStateChangeListener(listener); } }); }
@Test public void testUnsubscribeAfterTake() { final Subscription s = mock(Subscription.class); TestObservableFunc f = new TestObservableFunc("one", "two", "three"); Observable<String> w = Observable.create(f); @SuppressWarnings("unchecked") Observer<String> observer = mock(Observer.class); Subscriber<String> subscriber = Subscribers.from(observer); subscriber.add(s); Observable<String> take = w.lift(new OperatorTake<String>(1)); take.subscribe(subscriber); // wait for the Observable to complete try { f.t.join(); } catch (Throwable e) { e.printStackTrace(); fail(e.getMessage()); } System.out.println("TestObservable thread finished"); verify(observer, times(1)).onNext("one"); verify(observer, never()).onNext("two"); verify(observer, never()).onNext("three"); verify(observer, times(1)).onCompleted(); verify(s, times(1)).unsubscribe(); verifyNoMoreInteractions(observer); }
@Override public void call(final Subscriber<? super Bitmap> subscriber) { Preconditions.checkUiThread(); final Target target = new TargetAdapter() { @Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { if (!subscriber.isUnsubscribed()) subscriber.onNext(bitmap); } @Override public void onBitmapFailed(Drawable errorDrawable) { if (!subscriber.isUnsubscribed()) subscriber.onError(new PicassoError(errorDrawable)); } }; Picasso.with(context) .load(resId) .noFade() .memoryPolicy(NO_CACHE, NO_STORE) .config(Bitmap.Config.ARGB_8888) .transform(new PicassoTransformation(context, radius)) .into(target); subscriber.add( new MainThreadSubscription() { @Override protected void onUnsubscribe() { Picasso.with(context).cancelRequest(target); PicassoBlurOnSubscribe.this.context = null; } }); }
@Test public void onErrorSuccessWithUnsubscribeFailure() { AtomicReference<Throwable> onError = new AtomicReference<Throwable>(); Subscriber<String> o = OBSERVER_SUCCESS(onError); try { o.add( Subscriptions.create( new Action0() { @Override public void call() { // break contract by throwing exception throw new SafeObserverTestException("failure from unsubscribe"); } })); new SafeSubscriber<String>(o).onError(new SafeObserverTestException("failed")); fail("we expect the unsubscribe failure to cause an exception to be thrown"); } catch (Exception e) { e.printStackTrace(); assertTrue(o.isUnsubscribed()); // we still expect onError to have received something before unsubscribe blew up assertNotNull(onError.get()); assertTrue(onError.get() instanceof SafeObserverTestException); assertEquals("failed", onError.get().getMessage()); // now assert the exception that was thrown assertTrue(e instanceof SafeObserverTestException); assertEquals("failure from unsubscribe", e.getMessage()); } }
@Override public void call(final Subscriber<? super Void> subscriber) { checkUiThread(); SearchEditText.OnKeyboardDismissListener listener = new SearchEditText.OnKeyboardDismissListener() { @Override public void onKeyboardDismiss() { if (!subscriber.isUnsubscribed()) { subscriber.onNext(null); } } }; view.setOnKeyboardDismissListener(listener); subscriber.add( new MainThreadSubscription() { @Override protected void onUnsubscribe() { // TODO: set to null once http://b.android.com/187101 is released. view.setOnKeyboardDismissListener( new SearchEditText.OnKeyboardDismissListener() { @Override public void onKeyboardDismiss() {} }); } }); }
@Override public void call(final Subscriber<? super Void> subscriber) { verifyMainThread(); final ViewTreeObserver.OnPreDrawListener listener = new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { if (!subscriber.isUnsubscribed()) { subscriber.onNext(null); return proceedDrawingPass.call(); } return true; } }; view.getViewTreeObserver().addOnPreDrawListener(listener); subscriber.add( new MainThreadSubscription() { @Override protected void onUnsubscribe() { view.getViewTreeObserver().removeOnPreDrawListener(listener); } }); }
@Override public void call(final Subscriber<? super MenuItem> subscriber) { verifyMainThread(); PopupMenu.OnMenuItemClickListener listener = new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { if (!subscriber.isUnsubscribed()) { subscriber.onNext(item); } return true; } }; view.setOnMenuItemClickListener(listener); subscriber.add( new MainThreadSubscription() { @Override protected void onUnsubscribe() { view.setOnMenuItemClickListener(null); } }); }
@Override public void call(final Subscriber<? super T> s) { if (state.casFirst(0, 1)) { final NotificationLite<T> nl = NotificationLite.instance(); // drain queued notifications before subscription // we do this here before PassThruObserver so the consuming thread can do this before // putting itself in the line of the producer BufferedObserver<? super T> buffered = (BufferedObserver<? super T>) state.observerRef; Object o; while ((o = buffered.buffer.poll()) != null) { nl.accept(s, o); } // register real observer for pass-thru ... and drain any further events received on first // notification state.setObserverRef(new PassThruObserver<T>(s, buffered.buffer, state)); s.add( Subscriptions.create( new Action0() { @Override public void call() { state.setObserverRef(Subscribers.empty()); } })); } else { s.onError(new IllegalStateException("Only one subscriber allowed!")); } }
@Override public void call(final Subscriber<? super View> subscriber) { verifyMainThread(); SwipeDismissBehavior.OnDismissListener listener = new SwipeDismissBehavior.OnDismissListener() { @Override public void onDismiss(View view) { if (!subscriber.isUnsubscribed()) { subscriber.onNext(view); } } @Override public void onDragStateChanged(int i) {} }; if (!(view.getLayoutParams() instanceof LayoutParams)) { throw new IllegalArgumentException("The view is not in a Coordinator Layout."); } LayoutParams params = (LayoutParams) view.getLayoutParams(); final SwipeDismissBehavior behavior = (SwipeDismissBehavior) params.getBehavior(); if (behavior == null) { throw new IllegalStateException("There's no behavior set on this view."); } behavior.setListener(listener); subscriber.add( new MainThreadSubscription() { @Override protected void onUnsubscribe() { behavior.setListener(null); } }); }
@Override public void call(final Subscriber<? super FileEvent> subscriber) { final FileObserver observer = new FileObserver(pathToWatch) { @Override public void onEvent(int event, String file) { if (subscriber.isUnsubscribed()) { return; } FileEvent fileEvent = FileEvent.create(event, file); subscriber.onNext(fileEvent); if (fileEvent.isDeleteSelf()) { subscriber.onCompleted(); } } }; observer.startWatching(); // START OBSERVING subscriber.add( Subscriptions.create( new Action0() { @Override public void call() { observer.stopWatching(); } })); }
@Test public void onCompleteSuccessWithUnsubscribeFailure() { Subscriber<String> o = OBSERVER_SUCCESS(); try { o.add( Subscriptions.create( new Action0() { @Override public void call() { // break contract by throwing exception throw new SafeObserverTestException("failure from unsubscribe"); } })); new SafeSubscriber<String>(o).onCompleted(); fail("expects exception to be thrown"); } catch (Exception e) { e.printStackTrace(); assertTrue(o.isUnsubscribed()); assertTrue(e instanceof SafeObserverTestException); assertEquals("failure from unsubscribe", e.getMessage()); // expected since onError fails so SafeObserver can't help } }
@Override public void call(final Subscriber<? super MotionEvent> subscriber) { checkUiThread(); View.OnTouchListener listener = new View.OnTouchListener() { @Override public boolean onTouch(View v, @NonNull MotionEvent event) { if (handled.call(event)) { if (!subscriber.isUnsubscribed()) { subscriber.onNext(event); } return true; } return false; } }; view.setOnTouchListener(listener); subscriber.add( new MainThreadSubscription() { @Override protected void onUnsubscribe() { view.setOnTouchListener(null); } }); }
@Override public void request(long n) { if (n < 0L) { throw new IllegalArgumentException("n >= 0 required but it was " + n); } if (n != 0 && compareAndSet(false, true)) { actual.add(onSchedule.call(this)); } }
@Override public void call(final Subscriber<? super T> subscriber) { final Realm realm = Realm.getInstance(context); thread = Thread.currentThread(); subscriber.add( Subscriptions.create( new Action0() { @Override public void call() { if (thread != null && !thread.isInterrupted()) { thread.interrupt(); } } })); boolean interrupted = false; boolean withError = false; T object = null; try { realm.beginTransaction(); object = get(realm); interrupted = thread.isInterrupted(); if (object != null && !interrupted) { realm.commitTransaction(); } else { realm.cancelTransaction(); } } catch (RuntimeException e) { realm.cancelTransaction(); subscriber.onError(new RealmException("Error during transaction.", e)); withError = true; } catch (Error e) { realm.cancelTransaction(); subscriber.onError(e); withError = true; } if (!interrupted && !withError) { subscriber.onNext(object); } try { realm.close(); } catch (RealmException ex) { subscriber.onError(ex); withError = true; } thread = null; if (!withError) { subscriber.onCompleted(); } if (interrupted) { Thread.currentThread().interrupt(); } }
@Override public Subscriber<? super Observable<? extends T>> call(Subscriber<? super T> child) { final SerializedSubscriber<T> s = new SerializedSubscriber<T>(child); final CompositeSubscription csub = new CompositeSubscription(); child.add(csub); SourceSubscriber<T> ssub = new SourceSubscriber<T>(maxConcurrency, s, csub); child.setProducer(new MergeMaxConcurrentProducer<T>(ssub)); return ssub; }
@Override public Subscriber<? super T> call(final Subscriber<? super T> child) { final ParentSubscriber parent = new ParentSubscriber(child); child.add(parent); // don't unsubscribe downstream child.setProducer( new Producer() { @Override public void request(long n) { parent.downstreamRequest(n); } }); return parent; }
SwitchSubscriber(Subscriber<? super T> child) { serializedChild = new SerializedSubscriber<T>(child); arbiter = new ProducerArbiter(); ssub = new SerialSubscription(); child.add(ssub); child.setProducer( new Producer() { @Override public void request(long n) { if (n > 0) { arbiter.request(n); } } }); }
@Test public void onErrorFailureWithUnsubscribeFailure() { Subscriber<String> o = OBSERVER_ONERROR_FAIL(); try { o.add( Subscriptions.create( new Action0() { @Override public void call() { // break contract by throwing exception throw new SafeObserverTestException("failure from unsubscribe"); } })); new SafeSubscriber<String>(o).onError(new SafeObserverTestException("onError failure")); fail("expects exception to be thrown"); } catch (Exception e) { e.printStackTrace(); assertTrue(o.isUnsubscribed()); // assertions for what is expected for the actual failure propagated to onError which then // fails assertTrue(e instanceof RuntimeException); assertEquals( "Error occurred when trying to propagate error to Observer.onError and during unsubscription.", e.getMessage()); Throwable e2 = e.getCause(); assertTrue(e2 instanceof CompositeException); assertEquals( "Chain of Causes for CompositeException In Order Received =>", e2.getCause().getMessage()); Throwable e3 = e2.getCause(); assertTrue(e3.getCause() instanceof SafeObserverTestException); assertEquals("onError failure", e3.getCause().getMessage()); Throwable e4 = e3.getCause(); assertTrue(e4.getCause() instanceof SafeObserverTestException); assertEquals("onErrorFail", e4.getCause().getMessage()); Throwable e5 = e4.getCause(); assertTrue(e5.getCause() instanceof SafeObserverTestException); assertEquals("failure from unsubscribe", e5.getCause().getMessage()); } }
@Override public void call(final Subscriber<? super T> subscriber) { observerRef = subscriber; source .observeOn(AndroidSchedulers.mainThread()) .subscribe( new Subscriber<T>(subscriber) { @Override public void onCompleted() { if (componentRef != null && isComponentValid(componentRef)) { observerRef.onCompleted(); } else { unsubscribe(); log("onComplete: target component released or detached; dropping message"); } } @Override public void onError(Throwable e) { if (componentRef != null && isComponentValid(componentRef)) { observerRef.onError(e); } else { unsubscribe(); log("onError: target component released or detached; dropping message"); } } @Override public void onNext(T args) { if (componentRef != null && isComponentValid(componentRef)) { observerRef.onNext(args); } else { unsubscribe(); log("onNext: target component released or detached; dropping message"); } } }); subscriber.add( Subscriptions.create( new Action0() { @Override public void call() { log("unsubscribing from source sequence"); releaseReferences(); } })); }
@Override public Subscriber<? super T> call(final Subscriber<? super T> child) { // needs to independently unsubscribe so child can continue with the resume Subscriber<T> s = new Subscriber<T>() { private boolean done = false; @Override public void onNext(T t) { if (done) { return; } child.onNext(t); } @Override public void onError(Throwable e) { if (done) { Exceptions.throwIfFatal(e); return; } done = true; if (e instanceof Exception) { RxJavaPlugins.getInstance().getErrorHandler().handleError(e); unsubscribe(); resumeSequence.unsafeSubscribe(child); } else { child.onError(e); } } @Override public void onCompleted() { if (done) { return; } done = true; child.onCompleted(); } }; child.add(s); return s; }
@Override public void call(final Subscriber<? super T> subscriber) { try { // create the resource final Resource resource = resourceFactory.call(); // create an action/subscription that disposes only once final DisposeAction<Resource> disposeOnceOnly = new DisposeAction<Resource>(dispose, resource); // dispose on unsubscription subscriber.add(disposeOnceOnly); // create the observable final Observable<? extends T> source = observableFactory // create the observable .call(resource); final Observable<? extends T> observable; // supplement with on termination disposal if requested if (disposeEagerly) observable = source // dispose on completion or error .doOnTerminate(disposeOnceOnly); else observable = source; try { // start observable.unsafeSubscribe(Subscribers.wrap(subscriber)); } catch (Throwable e) { Throwable disposeError = disposeEagerlyIfRequested(disposeOnceOnly); Exceptions.throwIfFatal(e); Exceptions.throwIfFatal(disposeError); if (disposeError != null) subscriber.onError(new CompositeException(Arrays.asList(e, disposeError))); else // propagate error subscriber.onError(e); } } catch (Throwable e) { // then propagate error Exceptions.throwOrReport(e, subscriber); } }
@Override public void call(Subscriber<? super T> t) { final SerialSubscription serial = new SerialSubscription(); t.add(serial); final Subscriber<T> child = Subscribers.wrap(t); Subscriber<U> otherSubscriber = new Subscriber<U>() { boolean done; @Override public void onNext(U t) { onCompleted(); } @Override public void onError(Throwable e) { if (done) { RxJavaPlugins.getInstance().getErrorHandler().handleError(e); return; } done = true; child.onError(e); } @Override public void onCompleted() { if (done) { return; } done = true; serial.set(Subscriptions.unsubscribed()); main.unsafeSubscribe(child); } }; serial.set(otherSubscriber); other.unsafeSubscribe(otherSubscriber); }
@Override public void call(final Subscriber<? super Void> subscriber) { checkUiThread(); SwipeRefreshLayout.OnRefreshListener listener = new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { subscriber.onNext(null); } }; subscriber.add( new MainThreadSubscription() { @Override protected void onUnsubscribe() { view.setOnRefreshListener(null); } }); view.setOnRefreshListener(listener); }
@Override public void call(final Subscriber<? super RatingBarChangeEvent> subscriber) { checkUiThread(); RatingBar.OnRatingBarChangeListener listener = new RatingBar.OnRatingBarChangeListener() { @Override public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) { if (!subscriber.isUnsubscribed()) { subscriber.onNext(RatingBarChangeEvent.create(ratingBar, rating, fromUser)); } } }; subscriber.add( new MainThreadSubscription() { @Override protected void onUnsubscribe() { view.setOnRatingBarChangeListener(null); } }); view.setOnRatingBarChangeListener(listener); }
@Override public Subscriber<? super Observable<? extends T>> call(final Subscriber<? super T> child) { SwitchSubscriber<T> sws = new SwitchSubscriber<T>(child); child.add(sws); return sws; }
public static void main(String[] args) { Observable<String> lineInput = Observable.create( (Subscriber<? super String> subscriber) -> { if (subscriber.isUnsubscribed()) return; BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String line = null; try { while (!subscriber.isUnsubscribed() && (line = reader.readLine()) != null) { subscriber.onNext(line); } if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } } catch (IOException e) { subscriber.onError(e); } }); ConnectableObservable<String> sourceObs = lineInput.publish(); Function<Pattern, Observable<Integer>> lineParser = (pattern) -> sourceObs .filter( line -> { return pattern.matcher(line).matches(); }) .map( line -> { return Integer.parseInt(line.substring(line.indexOf("=") + 1).trim()); }); Subscriber<Integer> whenUnsub = new SafeSubscriber<Integer>( new Subscriber<Integer>() { @Override public void onCompleted() {} @Override public void onError(Throwable throwable) {} @Override public void onNext(Integer s) { System.out.println(s); } }); whenUnsub.add( Subscriptions.create( () -> { System.out.println("unsubscribe callback"); })); Observable.combineLatest( lineParser.apply(Pattern.compile("^a.*")).startWith(0), lineParser.apply(Pattern.compile("^b.*")).startWith(0), (x, y) -> x + y) .subscribe(whenUnsub); sourceObs.connect(); }
@Override public void call(final Subscriber<? super View> subscriber) { final SubscriptionAdapter adapter = new SubscriptionAdapter(subscriber, view); subscriber.add(adapter); view.addOnAttachStateChangeListener(adapter); }
@Override public void call(Subscriber<? super String> observer) { this.observer = observer; observer.add(s); }