I have a simple operation where an api call is being performed and the result is an Observable which emits the response:
apiService.getTeam()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Response>() {
...
});
I'd like to provide some feedback to the user if no items are emitted after a few seconds. I am aware of the .timeout() operator, however this would be applied to each item, so even if a few items are emitted, a few seconds after the last available item is emitted the timeout would cause an error.
I would only like to timeout the operation if no items are emitted. Is there a simple way to do this?
There is a timeout overload that you can use to timeout the first element and not timeout the rest:
Observable.never()
.timeout(() -> Observable.timer(1, TimeUnit.SECONDS), e -> Observable.never())
.toBlocking()
.subscribe(System.out::println, Throwable::printStackTrace);
Here, the second parameter simply returns an Observable that will never emit and thus subsequent elements won't timeout.
The below snippet can solve your problem:
public static void main(String[] args) {
boolean itemEmitted[] = { false };
long identifier = -1l;
Observable.interval(10, TimeUnit.SECONDS)
.mergeWith(Observable.just(identifier).delay(5, TimeUnit.SECONDS))
.map(v -> {
if (!itemEmitted[0] && v == identifier) { // item is not emitted and a timeout occurs
throw new RuntimeException("timeout");
}
itemEmitted[0] = true;
return v;
}).filter(v -> v != identifier).toBlocking()
.subscribe(v -> System.out.println(v));
}
Observable.interval is your source Observable that emits items which is merged with Observable.just with delay - whose combination acts similar to timeout. The output Observable is mapped to check if an actual item is emitted or whether timeout identifier occurred. If timeout, throw Exception. Later the output is filtered to remove the identifier from the list of emitted items.
You can play with this code by modifying the value in initial Observable.interval.
Related
I need to call a method with different input parameters (like id) asynchronously, the method returns true or false, and the false output is not desirable, so I should wait for a true output. As soon as I get a true one, I do not care about the other calls, just I need to know the true output is corresponding with which input (id).
I call this piece of code, how should I know the input id of responded method? It might have a more completableFuture. The next question is in case of getting false, how can I skip and wait for getting true, because I need to recieve true from one of the myService.myMethod(int input)?
CompletableFuture<Boolean> completableFuture= CompletableFuture.supplyAsync(() -> myService.myMethod(1));
CompletableFuture<Boolean> completableFuture2= CompletableFuture.supplyAsync(() -> myService.myMethod(2));
CompletableFuture<Boolean> completableFuture3= CompletableFuture.supplyAsync(() -> myService.myMethod(3));
CompletableFuture<Boolean> completableFuture4= CompletableFuture.supplyAsync(() -> myService.myMethod(4));
CompletableFuture<Object> result =
CompletableFuture.anyOf(completableFuture, completableFuture2,completableFuture3,,completableFuture4).thenApply(s -> s);
The fact that each result can be false makes this a bit tricky, because you can't abort too early. The following should work; it lets each CompletableFuture publish its results to a queue, as well as a sentinel value that indicates that all work is done. You then take the first element from the queue that was successful; if there is none, you know that all results are false if you read the sentinel value.
record Result (int id, boolean value) {}
final Result sentinel = new Result(Integer.MIN_VALUE, false);
// one more than the number of results, for the sentinel
BlockingDeque<Result> results = new LinkedBlockingDeque<>(5);
// the number of results exactly
CountDownLatch latch = new CountDownLatch(4);
CompletableFuture.runAsync(() -> {
Record record = new Record(1, myService.myMethod(1));
results.add(record);
latch.countDown();
});
// etc for 2-4
CompletableFuture.runAsync(() -> {
try {
latch.await();
} catch (InterruptedException e) {
// log or something?
} finally {
results.add(sentinel);
}
});
Result result = results.take();
while (!result.value() && !result.equals(sentinel)) {
result = results.take();
}
The sentinel is only added once each result has been published (due to the CountDownLatch), ensuring it's always the last element.
When the loop ends, if result.equals(sentinel), all results were false. Otherwise, result contains the id for the first available successful result.
I have a list a want to refresh every minute.
For example the user list here : https://github.com/android10/Android-CleanArchitecture/blob/master/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java
I add a periodical refresh using repeatWhen :
public Observable<List<User>> buildUseCaseObservable(Void unused) {
return this.userRepository
.users()
.repeatWhen(new Function<Observable<Object>, ObservableSource<?>>() {
#Override
public ObservableSource<?> apply(Observable<Object> objectObservable) throws Exception {
return objectObservable.delay(1, TimeUnit.MINUTES);
}
});
}
It works fine this way, calling onNext every minute.
But if I want to refresh immediately this list (because of user's action or because of a notification), I don't know how to perform that.
Should I cancel/dispose the observable and restart a new one ?
Thanks
From your code I understand that the users list is generated and emitted upon subscription.
Here are some solutions I can think of, instead of unsubscribing and resubscribing upon the event to which you want to react immediately:
Instead of using the repeatWhen operator, use the interval creation operator combined with the flatMap to invoke the subscription to a new Observable every minute and use the merge operator to add reaction to the other event in which you are interested. Something like this:
#Test
public void intervalObservableAndImmediateReaction() throws InterruptedException {
Observable<String> obs = Observable.interval(1, TimeUnit.SECONDS)
.cast(Object.class)
.mergeWith(
Observable.just("mockedUserClick")
.delay(500, TimeUnit.MILLISECONDS))
.flatMap(
timeOrClick -> Observable.just("Generated upon subscription")
);
obs.subscribe(System.out::println);
Thread.currentThread().sleep(3000); //to see the prints before ending the test
}
or adjusted to your needs (but the principal is the same):
Observable.interval(1, TimeUnit.MINUTES)
.mergeWith(RxView.clicks(buttonView))
.flatMap(timeOrClick -> this.userRepository.users());
You can use the flatMap operator as before, even while keeping you working current implementation and without merging to an interval - just keep your working code and in another area of the programme chain it to the RxBinding of your choosing:
RxView.touches(yourViewVariable)
.flatMatp(motionEvent -> this.userRepository.users())
.subscribe(theObserver);
Note that in this solution the subscription is done independently to the two observables. You'll probably be better off if you use different observers, or manage a subject or something on that line. A small test I ran showed one subscriber handled subscribing to 2 different observables with no problem (in Rxjava1 - didn't check in Rxjava2 yet), but it feels iffy to me.
If you aren't concerned with adjusting the refresh time after one of the other observables emits data you can do something like the following:
// Specific example of a user manually requesting
val request = Observable.create<String> { emitter ->
refresh.setOnClickListener {
emitter.onNext("Click Request")
}
}
.observeOn(Schedulers.io())
.flatMap {
userRepository.users()
}
// Refresh based off of your original work, could use something like interval as well
val interval = userRepository.users()
.subscribeOn(Schedulers.io())
.repeatWhen { objectObservable ->
objectObservable.delay(1, TimeUnit.MINUTES)
}
// Combine them so that both emissions are received you can even add on another source
Observable.merge(request,interval)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
contents.text = it.toString()
}, {
contents.text = it.toString()
},{
println(contents.text)
})
Then you don't have to dispose and resubscribe every time
I have following requirements.
CreateDocument
For document create many release notes (releaseNotesFuture)
For document create many parcels (parcelsFuture)
return objectId of document created in 1.
this is my current code:
public CompletableFuture<ObjectId> createDeliveryNoteDocument(String productId, List<String> releaseNotesIds) {
CompletableFuture<ObjectId> deliveryNoteFuture =
CompletableFuture
.supplyAsync(() -> sequenceServiceFeignClient.getNextValueForSequenceNameNoResponseEntity(DocumentType.DELIVERYNOTE.toString()))
.whenComplete((result, error) -> {
if (error != null)
logger.error("Unable to get next sequence number for DELIVERYNOTE sequence", error);
})
.thenCompose(seqNumber -> {
Set<ObjectAttribute> objectAttributes = new HashSet<>();
objectAttributes.add(new ObjectAttribute(Constants.Document.DOCUMENT_TYPE, DocumentType.DELIVERYNOTE.toString()));
objectAttributes.add(new ObjectAttribute(Constants.Document.DOCUMENT_NO, seqNumber));
objectAttributes.add(new ObjectAttribute(Constants.Document.PRODUCT_ID, productId));
return objectCommandService.createCustomObject(new ObjectTypeTableName(Constants.ObjectTables.DOCUMENT), objectAttributes);
});
CompletableFuture<Void> releaseNotesFuture =
deliveryNoteFuture
.thenComposeAsync(deliveryNoteId -> joinReleaseNotesWithDeliveryNote(deliveryNoteId, releaseNotesIds));
CompletableFuture<Void> parcelsFuture =
deliveryNoteFuture
.thenComposeAsync(deliveryNoteId -> changeParcelsStatusForReleaseNotes(releaseNotesIds));
return deliveryNoteFuture;
}
how could I wait for releaseNotesFuture and parcelsFuturecompletion and then return deliveryNoteFuture result or error if any of releaseNotesFuture or parcelsFuture finished exceptionally?
Instead of returning deliveryNoteFuture, you'll have to have a CompletableFuture that is completed when the releaseNotesFuture and parcelsFuture are completed. Furthermore, you'll want that future to then compose into the result of deliveryNoteFuture, since you want its ObjectId if the whole chain is successful.
Something like
return CompletableFuture.allOf(releaseNotesFuture, parcelsFuture).thenComposeAsync(r -> deliveryNoteFuture);
Since both releaseNotesFuture and parcelsFuture are dependent on deliveryNoteFuture, errors from deliveryNoteFuture will propagate through all these chains.
Similarly, if either of releaseNotesFuture or parcelsFuture fail, the CompletableFuture returned by allOf will be completed with that failure's cause and that cause will be propagated to the future returned by thenComposeAsync. Otherwise, the result of deliveryNoteFuture will be delivered.
As Holger suggests, since you only have those two futures, you could also use thenCombine
releaseNotesFuture.thenCombineAsync(parcelsFuture, (x, y) -> deliveryNoteFuture.join());
Here, the join() won't block since deliveryNoteFuture is definitely already completed. Again, if the deliveryNoteFuture had originally failed, its cause will be propagated down the chain ending with the CompletableFuture returned by thenCombineAsync.
I want to emit a random value 10 secs after onNext is called. So onNext will be called for 12345 and 10 secs later onNext should be called for a random number. What is the best way to achieve this using RxJava? Thanks in advance.
Observable.create(subscriber -> {
subscriber.onNext(12345);
}).subscribeOn(...)
.observeOn(...)
.subscribe(new Subscriber<Long>(){
#Override
public void onNext(Long aLong) {
//Do Something
}
})
If you emit your '12345' value right away, then solution is trivial. Just merge this observable with timer and you will get this kind of behavior. However, I am going to assume that your logic in OnSubscribe method you pass to create is doing some work and will emit the value once it is ready ( i.e. network request ). In that case, you need leverage both flatMap and merge operators. Something like this should suffice.
Observable.create(subscriber -> {
subscriber.onNext(12345);
})
.flatMap(value -> Observable.just(value)
.mergeWith(Observable.timer(10, TimeUnits.SECONDS).map(/* define your "random" value here */))
)
.subscribeOn(...)
.observeOn(...)
.subscribe(new Subscriber<Long>(){
#Override
public void onNext(Long aLong) {
//Do Something
}
})
It will take your value and convert it into and observable, which emits this value right away and starts a timer for 10 seconds to emit something else.
Note: Beware that this will take every item your observable emits and create this delayed second response for each of them. So if you plan on emitting multiple values from your source observable, you need to take this into account.
I feel like someone has to have tried this, but I can't figure out a nice way to do something if an observable takes to long.
This is the flow I want.
Start a search.
If the search takes longer than some time,
show a spinner or show progress bar.
When finished do subscription action and hide spinner/progress bar.
The closest I can think of is like a Zip
manager.search(searchTerm)
.zip(Observable.Timer(1, TimeUnit.SECONDS))
.subscribe(
// if the search is non null then we are good
// if the long time is non 0 we need to show spinner
);
Is there something better to do? I have been trying all day with no success. In a perfect world I feel like I would want something like
manager.search(searchTerm)
.timeout(i -> /* do timeout stuff */, 1, TimeUnit.SECONDS)
.subscribe(item -> /* do search result stuff */);
You can do this by publishing the search Observable through the timeout:
Observable<Integer> source = Observable.just(1).delay(5, TimeUnit.SECONDS);
source
.doOnSubscribe(() -> System.out.println("Starting"))
.publish(o ->
o.timeout(1, TimeUnit.SECONDS, Observable.<Integer>fromCallable(() -> {
System.out.println("Spinning...");
return null;
})).ignoreElements().mergeWith(o)
)
.toBlocking()
.subscribe(v -> {
System.out.println("Hide spinner if shown.");
System.out.println(v);
});
This works by splitting the source into two hot lanes: the first lane will run a timeout operator which when times out, starts another Observable with the side-effect that shows the spinning control. One of the ways is to use fromCallable for this and ignore its result (this also avoid duplication). The second lane will be unchanged and merged with the timeout lane to deliver the actual value.
Today i found a bit odd but working solution. Idea is to use interval instead of timer.
fun <T> base_delayed_progress_observable(source: Observable<T>): Observable<T>
{
val timer = Observable.interval(100, TimeUnit.MILLISECONDS) //Creates observable that will emit Long++ each 100 miliseconds
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(
{
if (it == 10L)//Here we check current timer value. For example here i check if it is 1 second gone (100 miliseconds * 10 = 1 second)
{
//here we put all we need to show progress/spinner an so on
}
})
return Observable.zip(source, timer,
BiFunction<T, Long, T> { t1, t2 ->
//Here we return our original Obervable zipped with timer
//Timer will be cancelled when our source Observable gets to OnComplete
return#BiFunction t1
}).doFinally(
{
//Here we can dismiss all progress dilogs/spinner
})
}