How do you show spinner if RxJava observable takes to long? - java

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
})
}

Related

when do you close a Subscriber whos emisor is an Observable with a debounce?

I am new on java rx and I was wondering if this is possible.
I have an observable with data that keeps coming from a controller.
And it does something like this:
observable.asObservable().debounce(10, SECONDS).subscribe(mySubscriber);
final Subscriber<Long> mySubscriber = new Subscriber<Long>() {
#Override
public void onCompleted() {
System.out.println("killing the subscriber");
}
#Override
public void onError(final Throwable throwable) {
}
#Override
public void onNext(final Long number) {
//some stuff
}
};
Works great, the debounce and the onNext, but when I stop sending events I was waiting the last debounce (with no info) will call the onCompleted() but it doesnt
I need to set a timeout after the last debounce is triggered, taking in consideration that an event can be published after N time.
For example.
If I have a debounce of 10 seconds and a timeout of 20.
I get first event after second 5.
Debounce is triggered and will wait another 10 seconds.
I get second event at second 14.
Debounce is triggered and I will wait until second 24.
Timeout is triggered at second 20 and kills the subscriber.
At second 24 I should get the event at the subscriber but was already killed.
I want to set the timeout AFTER the last emitted taking into consideration the debounce.
Not sure if what I am suggesting is the most elegant way, but you can just record last object received and then re-emit it on timeout like so
AtomicReference<Long> lastObject = new AtomicReference<>(null);
Observable
.interval(100, TimeUnit.MILLISECONDS)
.doOnNext(i -> lastObject.set(i))
.debounce(150, TimeUnit.MILLISECONDS)
.timeout(1000, TimeUnit.MILLISECONDS, Observable.create(emitter -> {
if (lastObject.get() != null) emitter.onNext(lastObject.get());
emitter.onComplete();
}))
.subscribe(...);
In this example we emit every 100 millis and since debounce is higher no items are emitted. So when timeout happens we re-emit last item that we have remembered in doOnNext before debounce.

RxJava polling + manual refresh

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

Timeout if no items are emitted after specified time

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.

Emit a value after onNext is called after a particular time

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.

RxJava doesn't work in Scheduler.io() thread

The problem in that: I have Observable and Subscriber. I try to launch Observable in .io() thread, because it works with files and zip archivers (I won't show the code - is too large), but Observable do nothing!:
Observable<Double> creatingObservable = getCreatingObservable(image);
Subscriber<Double> creatingSubscriber = getCreatingSubscriber();
creatingObservable
.subscribeOn(Schedulers.io())
.subscribe(creatingSubscriber);
If I launch code without the subscribeOn() - all work. What is the problem and how to solve it
P.S. System.out.println() doesn't work too. Problem have all Scheduler's threads.
It seems the problem is that the main thread terminated before creatingObservable could emit any values.
The simple solution: make the main thread wait long enough to enable creatingObservable to emit/complete.
Observable<Double> creatingObservable = getCreatingObservable(image);
Subscriber<Double> creatingSubscriber = getCreatingSubscriber();
creatingObservable
.subscribeOn(Schedulers.io())
.subscribe(creatingSubscriber);
Thread.sleep(5000); //to wait 5 seconds while creatingObservable is running on IO thread
Try this one:
Subscriber<Double> creatingSubscriber = getCreatingSubscriber();
Observable.defer(new Func0<Observable<Double>>() {
#Override
public Observable<Double> call() {
return getCreatingObservable(image);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(creatingSubscriber);
Don't forget to add:
compile 'io.reactivex:rxandroid:1.2.1'
From here: https://github.com/ReactiveX/RxAndroid
Explanation
getCreatingObservable(image); - most probably you use some operators which do 'hard' work in moment of call.
For example:
Observable.just(doSomeStuff())
.subscribeOn(...)
.observeOn(...)
So, the execution process will be:
1). Calculate doSomeStuff()
2). Pass result to Observable.just()
3). And only passing you are applying schedulers
In other words, you are doing 'hard' work firstly, and then applying schedulers.
That's why you need to use Observable.defer()
For more explanation, please read this article of Dan Lew:
http://blog.danlew.net/2014/10/08/grokking-rxjava-part-4/
Section Old, Slow Code
In this case you app create observable just once. You may try to either use
Observable.defer(()-> creatingObservable) so .defer operator will force observable creation every time.
Observable.defer(new Func0<Observable<Double>>() {
#Override
public Observable<Double> call() {
return getCreatingObservable();
}
})
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(getCreatingSubscriber);

Categories

Resources