Cancel task on timeout in RxJava - java

I'm experimenting with RxJava and Java 8's CompletableFuture class
and do not quite get how to handle timeout conditions.
import static net.javacrumbs.futureconverter.java8rx.FutureConverter.toObservable;
// ...
Observable<String> doSomethingSlowly() {
CompletableFuture<PaymentResult> task = CompletableFuture.supplyAsync(() -> {
// this call may be very slow - if it takes too long,
// we want to time out and cancel it.
return processor.slowExternalCall();
});
return toObservable(task);
}
// ...
doSomethingSlowly()
.single()
.timeout(3, TimeUnit.SECONDS, Observable.just("timeout"));
This basically works (if the timeout of three seconds is reached, "timeout" is published). I would however additionally want to cancel the future task that I've wrapped in an Observable - is that possible with an RxJava-centric approach?
I know that one option would be to handle the timeout myself, using task.get(3, TimeUnit.SECONDS), but I wonder if it's possible to do all task handling stuff in RxJava.

Yes, you can do this. You would add a Subscription to the Subscriber.
This allows you to listen in on unsubscriptions, which will happen if you explicitly call subscribe().unsubscribe() or if the Observable completes successfully or with an error.
If you see an unsubscription before the future has completed, you can assume it's because of either an explicit unsubscribe or a timeout.
public class FutureTest {
public static void main(String[] args) throws IOException {
doSomethingSlowly()
.timeout(1, TimeUnit.SECONDS, Observable.just("timeout"))
.subscribe(System.out::println);
System.in.read(); // keep process alive
}
private static Observable<String> doSomethingSlowly() {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
return "Something";
});
return toObservable(future);
}
private static <T> Observable<T> toObservable(CompletableFuture<T> future) {
return Observable.create(subscriber -> {
subscriber.add(new Subscription() {
private boolean unsubscribed = false;
#Override
public void unsubscribe() {
if (!future.isDone()){
future.cancel(true);
}
unsubscribed = true;
}
#Override
public boolean isUnsubscribed() {
return unsubscribed;
}
});
future.thenAccept(value -> {
if (!subscriber.isUnsubscribed()){
subscriber.onNext(value);
subscriber.onCompleted();
}
}).exceptionally(throwable -> {
if (!subscriber.isUnsubscribed()) {
subscriber.onError(throwable);
}
return null;
});
});
}
}

Related

How to Interrupt/Abort Rx Java Observable Chain Subscription?

I have a requirement to abort the Rx Java Observable chain, Take Until helps partially but does a graceful completion of Job. I need a handle onAborted on all the the observables part of this chain. Dispose/CompositeDispose none of them are helping to delegate the onAborted handle upward in observable.
Note: The RxJava is not being used here for UI Operations, rather to process/transform shorter data streams in memory.
dataObservable.map(row -> convertRow())
//.takeUntil(getAbortPredicate()/*return true when abort set to true*/)
.collect(Collectors.toList())
.doFinally(getResetAbortAction()/*resets abort flag*/)
.blockingGet()
The dataObservable itself here is chain of observables. I want to interrupt all such observables involved & get a handle on abort for each of them.
public class A {
private Observable<Row> observable;
private Boolean aborted;
private SomeUtilityWithResources utilty
public A(Observable<Row> observable){
this.observable = observable;
this.aborted = false;
this.utilty = SomeUtilityWithResources.Init();
}
public A mergeOp1(A aRight){
Observable<Row> merged = observable.flatMap(lr -> aRight.observable.map(rr -> utilty.merge1(lr.toList(), rr.toList())));
//When abruptly aborted I want to call utilty.resources.abort().... something like joined.OnAborted(utility.resources.abort())
//Tried OnDispose()/OnError doesn't get invoked though
//utilty.merge1 also returns an Observable
return new A(merged)
}
public A mergeOp2(A aRight){
Observable<Row> merged = observable.flatMap(lr -> aRight.observable.map(rr -> utilty.merge2(lr.toList(), rr.toList())));
//When abruptly aborted I want to call utilty.resources.abort().... something like joined.OnAborted(utility.resources.abort())
//Tried OnDispose()/OnError doesn't get invoked though
//utilty.merge2 also returns an Observable
return new A(merged)
}
public A mergeOp3(A aRight){
Observable<Row> merged = observable.flatMap(lr -> aRight.observable.map(rr -> utilty.merge3(lr.toList(), rr.toList())));
//When abruptly aborted I want to call utilty.resources.abort().... something like joined.OnAborted(utility.resources.abort())
//Tried OnDispose()/OnError doesn't get invoked though
//utilty.merge3 also returns an Observable
return new A(merged)
}
public List<Row> execWay1(){
observable.map(row -> convertRowWay1())
//.takeUntil(getAbortPredicate()/*return true when abort set to true*/)
.collect(Collectors.toList())
.doFinally(getResetAbortAction()/*resets abort flag*/)
.blockingGet()
}
public Long count(){
observable
//.takeUntil(getAbortPredicate()/*return true when abort set to true*/)
.count()
.doFinally(getResetAbortAction(action.toString()))
.blockingGet()
}
public void abort(){
this.aborted = true;
}
private Action getResetAbortAction(String action) {
return new Action() {
#Override
public void run() throws Throwable {
if (aborted.get()) {
aborted.set(false);
//LOGGER.error(action + " execution aborted successfully!");
//throw new Exception("Aborted")
}
}
};
}
private Predicate<T> getAbortPredicate() {
return new Predicate<T>() {
#Override
public boolean test(T t) throws Exception {
return aborted.get();
}
};
}
}
/* Executor would be calling:
A a = SomeUtilityWithResources.getDefaultObservableFromSomeSource();
a = a.mergeOp1.mergeOp2.mergedOp3
a.count()
//if taking longer.... a.abort() on seperate thread;
a.execWay1();

How to implement a nonblocking future processor in Java in order to retrieve processed results later

I am using an external library that has the code from below. I am sending a lot of commands and am interesed in the result for statistics to check how many calls failed and how many succeeded
public Future<CommandResult> sendCommand(Command command) {
return command.execute();
}
CommandResult can be success or failure
However, if I use client.sendCommand(command).get(); then, I am waiting for the result synchronously, meanwhile the app is being blocked.
I would like to check only later (after 30 seconds which calls succeded and which failed). I am guaranteed to get an answer in 10 seconds.
The problem is that the app waits for the computation to complete, and then retrieves its result.
I was thinking about this approach based on the answers:
List<Future< CommandResult >> futures = new ArrayList<>();
for(Command command: commands) {
futures.add(client.sendCommand(command));
}
//in a scheduler, 30+ seconds later
for (Future<Boolean> future : futures) {
saveResult(future.get());
}
I would like to check only later (after 30 seconds which calls succeeded and which failed). I am guaranteed to get an answer in 10 seconds. The problem is that the app waits for the computation to complete, and then retrieves its result.
If you want check on the results at a later time then your solution with Future<Boolean> should be fine. The jobs will run in the background and you will get the results form then when you call future.get(). Each of those get() calls do block however.
If you want to get the results as they come in, I would use an ExecutorCompletionService which you can poll anytime to see if you have results. The poll is non-blocking.
// create your thread pool using fixed or other pool
Executor<Result> threadPool = Executors.newFixedThreadPool(5);
// wrap the Executor in a CompletionService
CompletionService<Boolean> completionService =
new ExecutorCompletionService<>(e);
// submit jobs to the pool through the ExecutorCompletionService
for (Job job : jobs) {
completionService.submit(job);
}
// after we have submitted all of the jobs we can shutdown the Executor
// the jobs submitted will continue to run
threadPool.shutdown();
...
// some point later you can do
int jobsRunning = jobs.size();
for (int jobsRunning = jobs.size(); jobsRunning > 0; ) {
// do some processing ...
// are any results available?
Boolean result = completionService.poll();
if (result != null) {
// process a result if available
jobsRunning--;
}
}
Note that you will need to track how many jobs you submitted to the CompletionService.
Future is a legacy java feature which does not allow for reactive non blocking functionalities. The CompletableFuture is a later enhancement in Java in order to allow such reactive non blocking functionalities.
You can based on this previous SO answer try to convert your Future into a CompletableFuture and then you will have methods exposed to take advantage of non blocking execution.
Check the following example and modify accordingly.
public class Application {
public static void main(String[] args) throws ParseException {
Future future = new SquareCalculator().calculate(10);
CompletableFuture<Integer> completableFuture = makeCompletableFuture(future);
System.out.println("before apply");
completableFuture.thenApply(s -> {
System.out.println(s);
return s;
});
System.out.println("after apply method");
}
public static <T> CompletableFuture<T> makeCompletableFuture(Future<T> future) {
if (future.isDone())
return transformDoneFuture(future);
return CompletableFuture.supplyAsync(() -> {
try {
if (!future.isDone())
awaitFutureIsDoneInForkJoinPool(future);
return future.get();
} catch (ExecutionException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
// Normally, this should never happen inside ForkJoinPool
Thread.currentThread().interrupt();
// Add the following statement if the future doesn't have side effects
// future.cancel(true);
throw new RuntimeException(e);
}
});
}
private static <T> CompletableFuture<T> transformDoneFuture(Future<T> future) {
CompletableFuture<T> cf = new CompletableFuture<>();
T result;
try {
result = future.get();
} catch (Throwable ex) {
cf.completeExceptionally(ex);
return cf;
}
cf.complete(result);
return cf;
}
private static void awaitFutureIsDoneInForkJoinPool(Future<?> future)
throws InterruptedException {
ForkJoinPool.managedBlock(new ForkJoinPool.ManagedBlocker() {
#Override public boolean block() throws InterruptedException {
try {
future.get();
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
return true;
}
#Override public boolean isReleasable() {
return future.isDone();
}
});
}
}
And then the class to create an example Future
public class SquareCalculator {
private ExecutorService executor
= Executors.newSingleThreadExecutor();
public Future<Integer> calculate(Integer input) {
return executor.submit(() -> {
Thread.sleep(1000);
return input * input;
});
}
}
Will result into
If converting the Future instances to CompletableFuture (see answer from Panagiotis Bougioukos) is an option, then you can implement a simple helper function for turning a Stream<CompletableFuture<T>> into a CompletableFuture<Stream<T>>:
public static <T> CompletableFuture<Stream<T>> collect(Stream<CompletableFuture<T>> futures) {
return futures
.map(future -> future.thenApply(Stream::of))
.reduce(
CompletableFuture.completedFuture(Stream.empty()),
(future1, future2) ->
future1
.thenCompose(stream1 ->
future2
.thenApply(stream2 ->
concat(stream1, stream2)))
);
}
Essentially this reduces the stream of futures in parallel to a future of a stream.
If you use this e.g. on a stream of futures of strings, it will return a future that completes once the last of the individual futures completed:
Stream<CompletableFuture<String>> streamOfFutures = ...
CompletableFuture<Stream<String>> futureOfStream = collect(streamOfFutures);
// Prints a list of strings once the "slowest" future completed
System.out.println(futureOfStream.get().toList());

Avoid using CountDownLatch to wait for many threads before sending the callback

So I have a list of Track Ids that for each track Id I need to execute a network request to get the track details, I am using a for loop to launch all the requests and a latch to wait for all the requests to be completed. After they are completed then the callback is sent with the List of Tracks that have already populated.
I would like to know if there is any better way to do this, maybe with RxJava ?
I am using Retrofit 2.0 in Android.
public IBaseRequest batchTracksById(final TrackIdList trackIdListPayload, final IRequestListener<TracksList> listener) {
final TracksList tracks = new TracksList();
final Track[] trackArray = newrack[trackIdListPayload.getTrackIds().length];
tracks.setTrack(trackArray);
final CountDownLatch latch = new CountDownLatch(trackArray.length);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
latch.await();
handler.post(new Runnable() {
#Override
public void run() {
listener.onRequestUpdate(null, tracks, null, true);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
for (String id : trackIdListPayload.getTrackIds()) {
getTrackById(id, new IRequestListener<Track>() {
#Override
public void onRequestFailure(IBaseRequest request, Exception exception) {
latch.countDown();
}
#Override
public void onRequestUpdate(IBaseRequest request, Track track, RequestState state, boolean requestComplete) {
//iterate through the tracks and update the ones in the thing
int i = 0;
for (String s : trackIdListPayload.getTrackIds()) {
if (s.equals(track.getTrackId())) {
trackArray[i] = track;
// don't break here, as we may have a case where we have multiple instances of the same trackId (although
// at the moment a request will be made for each anyway...
}
i++;
}
latch.countDown();
}
});
}
return null;
}
If you want to make all the requests asynchronously and wait for them to return you can do this (lambdas for brevity and readability):
tracks.flatMap(track -> getTrackDetails(track.id)
.subscribeOn(Schedulers.io()))
.toList()
.doOnNext(list -> processTrackList())
...
If you require that the results are returned in the order of tracks but are still requested asynchronously then in soon to be released rxjava 1.0.15 you will be able to do this
tracks.concatMapEager(track -> getTrackDetails(track.id)
.subscribeOn(Schedulers.io())
.toList()
.doOnNext(list -> processTrackList())
...
If I understand correctly, you have a list of tracks as input and you want a list of webservice results. Here's a simple way to do that with RxJava if you can make your network call synchronous (rxjava will handle the background processing for you).
Observable.from(trackList)
.map(new Func1<Track, Response>() {
#Override
public Response call(Track track) {
return makeRequestSynchronously(track.id());
}
})
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<Response>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Response> responses) {
}
});
Edit: You can change Retrofit to return observables from webservice, if you do that you will need to change map to the following
.flatMap(new Func1<Track, Observable<Response>>() {
#Override
public Observable<Response> call(Track track) {
return makeRequestThatReturnsObservable(track.id());
}
})

How to interrupt underlying execution of CompletableFuture

I know that CompletableFuture design does not control its execution with interruptions, but I suppose some of you might have this problem. CompletableFutures are very good way to compose async execution, but given the case when you want the underlying execution to be interrupted or stopped when future is canceled, how do we do that? Or we must just accept that any canceled or manually completed CompletableFuture will not impact the thread working out there to complete it?
That is, in my opinion, obviously a useless work that takes time of executor worker. I wonder what approach or design might help in this case?
UPDATE
Here is a simple test for this
public class SimpleTest {
#Test
public void testCompletableFuture() throws Exception {
CompletableFuture<Void> cf = CompletableFuture.runAsync(()->longOperation());
bearSleep(1);
//cf.cancel(true);
cf.complete(null);
System.out.println("it should die now already");
bearSleep(7);
}
public static void longOperation(){
System.out.println("started");
bearSleep(5);
System.out.println("completed");
}
private static void bearSleep(long seconds){
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
System.out.println("OMG!!! Interrupt!!!");
}
}
}
A CompletableFuture is not related to the asynchronous action that may eventually complete it.
Since (unlike FutureTask) this class has no direct control over the
computation that causes it to be completed, cancellation is treated as
just another form of exceptional completion. Method cancel has the
same effect as completeExceptionally(new CancellationException()).
There may not even be a separate thread working on completing it (there may even be many threads working on it). Even if there is, there's no link from a CompletableFuture to any thread that has a reference to it.
As such, there's nothing you can do through CompletableFuture to interrupt any thread that may be running some task that will complete it. You'll have to write your own logic which tracks any Thread instances which acquire a reference to the CompletableFuture with the intention to complete it.
Here's an example of the type of execution I think you could get away with.
public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newFixedThreadPool(1);
CompletableFuture<String> completable = new CompletableFuture<>();
Future<?> future = service.submit(new Runnable() {
#Override
public void run() {
for (int i = 0; i < 10; i++) {
if (Thread.interrupted()) {
return; // remains uncompleted
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
return; // remains uncompleted
}
}
completable.complete("done");
}
});
Thread.sleep(2000);
// not atomic across the two
boolean cancelled = future.cancel(true);
if (cancelled)
completable.cancel(true); // may not have been cancelled if execution has already completed
if (completable.isCancelled()) {
System.out.println("cancelled");
} else if (completable.isCompletedExceptionally()) {
System.out.println("exception");
} else {
System.out.println("success");
}
service.shutdown();
}
This assumes that the task being executed is setup to handle interruptions correctly.
What about this?
public static <T> CompletableFuture<T> supplyAsync(final Supplier<T> supplier) {
final ExecutorService executorService = Executors.newFixedThreadPool(1);
final CompletableFuture<T> cf = new CompletableFuture<T>() {
#Override
public boolean complete(T value) {
if (isDone()) {
return false;
}
executorService.shutdownNow();
return super.complete(value);
}
#Override
public boolean completeExceptionally(Throwable ex) {
if (isDone()) {
return false;
}
executorService.shutdownNow();
return super.completeExceptionally(ex);
}
};
// submit task
executorService.submit(() -> {
try {
cf.complete(supplier.get());
} catch (Throwable ex) {
cf.completeExceptionally(ex);
}
});
return cf;
}
Simple Test:
CompletableFuture<String> cf = supplyAsync(() -> {
try {
Thread.sleep(1000L);
} catch (Exception e) {
System.out.println("got interrupted");
return "got interrupted";
}
System.out.println("normal complete");
return "normal complete";
});
cf.complete("manual complete");
System.out.println(cf.get());
I don't like the idea of having to create an Executor service every time, but maybe you can find a way to reuse the ForkJoinPool.
If you use
cf.get();
instead of
cf.join();
The thread waiting on the completion can be interrupted. This bit me in the a**, so I'm just putting it out there. You'd then need to propagate this interruption further / use cf.cancel(...) to really finish the execution.
I had similar issue wherein I needed to simulate a InterruptedException.
I mocked the method call that is supposed to return the CompletetableFuture, and I put a spy on return value such that CompletableFuture#get will throw the exception.
It worked as I expected, and I was able to test that code handled the exception correctly.
CompletableFuture spiedFuture = spy(CompletableFuture.completedFuture(null));
when(spiedFuture .get()).thenThrow(new InterruptedException());
when(servuce.getById(anyString())).thenReturn(spiedFuture );
Here is a ultra-short version to create a Future task that can be cancelled:
public static <T> Future<T> supplyAsync(Function<Future<T>, T> operation) {
CompletableFuture<T> future = new CompletableFuture<>();
return future.completeAsync(() -> operation.apply(future));
}
The CompletableFuture is passed to the operation Function to be able to check the cancel status of the Future:
Future<Result> future = supplyAsync(task -> {
while (!task.isCancelled()) {
// computation
}
return result;
});
// later you may cancel
future.cancel(false);
// or retrieve the result
Result result = future.get(5, TimeUnit.SECONDS);
This however does not interrupt the Thread running the operation. If you also want to be able to interrupt the Thread, then you have to store a reference to it and override Future.cancel(..) to interrupt it.
public static <T> Future<T> supplyAsync(Function<Future<T>, T> action) {
return supplyAsync(action, r -> new Thread(r).start());
}
public static <T> Future<T> supplyAsync(Function<Future<T>, T> action, Executor executor) {
AtomicReference<Thread> interruptThread = new AtomicReference<>();
CompletableFuture<T> future = new CompletableFuture<>() {
#Override
public boolean cancel(boolean mayInterruptIfRunning) {
if (!interruptThread.compareAndSet(null, Thread.currentThread())
&& mayInterruptIfRunning) {
interruptThread.get().interrupt();
}
return super.cancel(mayInterruptIfRunning);
}
};
executor.execute(() -> {
if (interruptThread.compareAndSet(null, Thread.currentThread())) try {
future.complete(action.apply(future));
} catch (Throwable e) {
future.completeExceptionally(e);
}
});
return future;
}
The following test checks that the Thread executing our Function got interrupted:
#Test
void supplyAsyncWithCancelOnInterrupt() throws Exception {
Object lock = new Object();
CountDownLatch done = new CountDownLatch(1);
CountDownLatch started = new CountDownLatch(1);
Future<Object> future = supplyAsync(m -> {
started.countDown();
synchronized (lock) {
try {
lock.wait(); // let's get interrupted
} catch (InterruptedException e) {
done.countDown();
}
}
return null;
});
assertFalse(future.isCancelled());
assertFalse(future.isDone());
assertTrue(started.await(5, TimeUnit.SECONDS));
assertTrue(future.cancel(true));
assertTrue(future.isCancelled());
assertTrue(future.isDone());
assertThrows(CancellationException.class, () -> future.get());
assertTrue(done.await(5, TimeUnit.SECONDS));
}
What about?
/** #return {#link CompletableFuture} which when cancelled will interrupt the supplier
*/
public static <T> CompletableFuture<T> supplyAsyncInterruptibly(Supplier<T> supplier, Executor executor) {
return produceInterruptibleCompletableFuture((s) -> CompletableFuture.supplyAsync(s, executor), supplier);
}
// in case we want to do the same for similar methods later
private static <T> CompletableFuture<T> produceInterruptibleCompletableFuture(
Function<Supplier<T>,CompletableFuture<T>> completableFutureAsyncSupplier, Supplier<T> action) {
FutureTask<T> task = new FutureTask<>(action::get);
return addCancellationAction(completableFutureAsyncSupplier.apply(asSupplier(task)), () ->
task.cancel(true));
}
/** Ensures the specified action is executed if the given {#link CompletableFuture} is cancelled.
*/
public static <T> CompletableFuture<T> addCancellationAction(CompletableFuture<T> completableFuture,
#NonNull Runnable onCancellationAction) {
completableFuture.whenComplete((result, throwable) -> {
if (completableFuture.isCancelled()) {
onCancellationAction.run();
}
});
return completableFuture; // return original CompletableFuture
}
/** #return {#link Supplier} wrapper for the given {#link RunnableFuture} which calls {#link RunnableFuture#run()}
* followed by {#link RunnableFuture#get()}.
*/
public static <T> Supplier<T> asSupplier(RunnableFuture<T> futureTask) throws CompletionException {
return () -> {
try {
futureTask.run();
try {
return futureTask.get();
} catch (ExecutionException e) { // unwrap ExecutionExceptions
final Throwable cause = e.getCause();
throw (cause != null) ? cause : e;
}
} catch (CompletionException e) {
throw e;
} catch (Throwable t) {
throw new CompletionException(t);
}
};
}

Is there an observable that just propagates the error without terminating itself?

I am using PublishSubject in the class that is responsible for synchronization. When the synchronization is done all the subscribers will be notified. The same happens in case of an error.
I've noticed that the next time I subscribe after an error has occured, it is immediately return to the subscriber.
So the class may look like this:
public class Synchronizer {
private final PublishSubject<Result> mSyncHeadObservable = PublishSubject.create();
private final ThreadPoolExecutor mExecutor = new ThreadPoolExecutor(1, 1,
10, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(true),
new ThreadPoolExecutor.DiscardPolicy());
public Observable<Result> syncHead(final int chunkSize) {
mExecutor.execute(new Runnable() {
#Override
public void run() {
try {
//Do some work which either returns a result or throws an error
//...
mSyncHeadObservable.onNext(Notification.createOnNext(/*some result*/));
} catch (Throwable error) {
mSyncHeadObservable.onError(Notification.<Result>createOnError(error));
}
}
});
Is there an observable which can just serve as an proxy? May be some other Rx approach?
UPDATE:
I've followed #akarnokd approach and emit the events wrapped into the RxJava Notification. Then unwrap them via flatMap(). So the clients of Synchronizer class won't need to do it.
//...
private PublishSubject<Notification<Result>> mSyncHeadObservable = PublishSubject.create();
public Observable<Result> syncHead(final int chunkSize) {
return mSyncHeadObservable.flatMap(new Func1<Notification<Result>, Observable<Result>>() {
#Override
public Observable<Result> call(Notification<Result> result) {
if (result.isOnError()) {
return Observable.error(result.getThrowable());
}
return Observable.just(result.getValue());
}
}).doOnSubscribe(
new Action0() {
#Override
public void call() {
startHeadSync(chunkSize);
}
});
}
private void startHeadSync(final int chunkSize) {
mExecutor.execute(new Runnable() {
#Override
public void run() {
try {
//Do some work which either returns a result or throws an error
//...
mSyncHeadObservable.onNext(Notification.createOnNext(/*some result*/));
} catch (Throwable error) {
mSyncHeadObservable.onError(Notification.<Result>createOnError(error));
}
}
});
}
//...
I'm not sure what your want to achieve with this setup, but generally, in order to avoid a terminal condition with PublishSubject, you should wrap your value and error into a common structure and always emit those, never any onError and onCompleted. One option is to use RxJava's own event wrapper, Notification, and your Subscribers should unwrap the value.
When a error occurred, the observable reached an terminal state.
If you want to continue to observe it, you should resubscribe to you observable with retry operator or use another error handling operators

Categories