Say I have 2 Observables (A & B) that are essentially network calls (using Retrofit to give context).
The current flow of the app is as follows:
A & B are kicked off at about the same time (asynchronously).
B is executed 0 or more times on user interaction
I have 3 different scenarios that I want to listen for given these 2 observables/api calls.
I want to know immediately when Observable A completes
I want to know immediately when Observable B completes
I want to know when both have completed
First off, is this a good use case for RxJava?
I know how to do each scenario individually (using zip for the last), though I don't know how to do all of them simultaneously.
If I subscribe to Observable A, A begins. If I subscribe to B, B begins. If A & B complete before I subscribe to zip(a, b), I could miss the event and never actually see this complete, right?
Any general guidance would be appreciated. My RxJava knowledge is pretty thin :P
You can achieve this using three different observable, one for each of your case.
As you'll have to share states between each observables, you'll have to convert retrofit cold observables to hot observable. (see here for more information on this topic)
ConnectableObservable a = service.callA().publish();
ConnectableObservable b = service.callB().publish();
a.subscribe((e) -> { /* onNext */ }, (ex) -> {/* onError */}, () -> {/* when A is completed */ });
b.subscribe((e) -> { /* onNext */ }, (ex) -> {/* onError */}, () -> {/* when B is completed */ });
a.mergeWith(b).subscribe((e) -> { /* onNext */ }, (ex) -> {/* onError */}, () -> {/* when A and B are completed */ });
a.connect(); // start subscription to a
b.connect(); // start subscription to b
Do not share an object between onCompleted methods or you'll have to deal with concurrencies issues.
Related
In an Android app scenario, I want to fetch some Observable<Data> from network, and there are multiple Observer<Data> subscribed to it to update corresponding views. In case of error -say a timeout- show a button to the user to try again.
How can I do the try again part? can I tell the observable to re-execute its logic again without re-subscribing to it?
Let's assume you have two buttons, "Retry" and "Cancel", initially hidden. Create two Observables retryButtonClicks and cancelButtonClicks. Then, apply the retryWhen operator to the designated download flow and act upon the signals of these button clicks:
download.retryWhen(errors -> {
return errors
.observeOn(AndroidSchedulers.mainThread())
.flatMap(e -> {
// show the "Retry" and "Cancel" buttons around here
return Observable.amb(
retryButtonClicks.take(1).map(v -> "Retry"),
cancelButtonClicks.take(1).map(v -> "Cancel")
)
.doOnNext(v -> { /* hide the "Retry" and "Cancel" buttons */ });
})
.takeWhile(v -> "Retry".equals(v))
;
});
There is actually specific methods
retry()
Returns an Observable that mirrors the source Observable,
resubscribing to it if it calls onError (infinite retry count).
and retry(long count)
Returns an Observable that mirrors the source Observable,
resubscribing to it if it calls onError up to a specified number of
retries.
Read more in an article and in the docs
I have an observable that on subscribe does a long operation but when a user click on a button I want to notify my observable to do again the long operation because something change.
I don't want to make a new subscription every time the user clicks on the button. Which is the best solution to achieve this?
I would like to know if is possible to use a solution, which use the rxjava simplified way to run code on different threads.
Should I use something like this?
BehaviourProcessor<boolean> processor = BehaviourProcessor.createDefault(true);
public Flowable<List<Item>> getItems(){
return Flowable.create(e -> e.onNext(longOp()))
.subscribeOn(Schedulers.io())
.switchMap(items -> processor.map(notify -> returnItems(notify)));
}
public void notifyChange(){
processor.onNext(true);
}
Android Room library achieve this result, in fact when you subscribe this:
#Query("SELECT * FROM user")
Flowable<List<Item>> getUsers();
Every time you delete an item from database you immediately get the new list from the database in the subscription on next method.
Rather than have getItems() return the observable chain that you have shown, return a shared observable.
Flowable<List<Item>> itemGetter =
Flowable.create(e -> e.onNext( longOp() ) )
.subscribeOn( Schedulers.io() )
.switchMap(items -> processor.map(notify -> returnItems(notify)))
.replay( 1 )
.publish();
Flowable<List<Item>> getItem() {
return itemGetter;
}
This creates only on observer chain that you can subscribe to as many times as you want. However, if there are no subscribers and another subscriber comes along, longOp() will be called again.
If you don't want that to happen, then you should consider using a BehaviorSubject<List<Item>> to cache the value.
What is the difference between
ObservableTransformer {
Observable.merge(
it.ofType(x).compose(transformerherex),
it.ofType(y).compose(transformerherey)
)
}
and
ObservableTransformer {
it.publish{ shared ->
Observable.merge(
shared.ofType(x).compose(transformerherex),
shared.ofType(y).compose(transformerherey)
)
}
}
when I run my code using this two, I got the same results. What does publish do here.
The difference is that the top transformer will subscribe to the upstream twice for a single subscription from the downstream, duplicating any side effects of the upstream which is usually not wanted:
Observable<Object> mixedSource = Observable.<Object>just("a", 1, "b", 2, "c", 3)
.doOnSubscribe(s -> System.out.println("Subscribed!"));
mixedSource.compose(f ->
Observable.merge(
f.ofType(Integer.class).compose(g -> g.map(v -> v + 1)),
f.ofType(String.class).compose(g -> g.map(v -> v.toUpperCase()))
)
)
.subscribe(System.out::println);
will print
Subscribed!
2
3
4
Subscribed!
A
B
C
The side-effect represented here is the printout Subscribed! Depending on the actual work in a real source, that could mean sending an email twice, retrieving the rows of a table twice. With this particular example, you can see that even if the source values are interleaved in their type, the output contains them separately.
In contrast, publish(Function) will establish one subscription to the source per one end subscriber, thus any side-effects at the source only happen once.
mixedSource.publish(f ->
Observable.merge(
f.ofType(Integer.class).compose(g -> g.map(v -> v + 1)),
f.ofType(String.class).compose(g -> g.map(v -> v.toUpperCase()))
)
)
.subscribe(System.out::println);
which prints
Subscribed!
A
2
B
3
C
4
because the source is subscribed once and each item is multicast to the two "arms" of the .ofType().compose().
publish operator converts your Observable to Connectable Observable.
Lets see what does Connectable Observable mean: Suppose you want to subscribe an observable multiple time and want to serve same items to each subscriber. You need to use Connectable Observable.
Example:
var period = TimeSpan.FromSeconds(1);
var observable = Observable.Interval(period).Publish();
observable.Connect();
observable.Subscribe(i => Console.WriteLine("first subscription : {0}", i));
Thread.Sleep(period);
observable.Subscribe(i => Console.WriteLine("second subscription : {0}", i));
output:
first subscription : 0
first subscription : 1
second subscription : 1
first subscription : 2
second subscription : 2
In this case, we are quick enough to subscribe before the first item is published, but only on the first subscription. The second subscription subscribes late and misses the first publication.
We could move the invocation of the Connect() method until after all subscriptions have been made. That way, even with the call to Thread.Sleep we will not really subscribe to the underlying until after both subscriptions are made. This would be done as follows:
var period = TimeSpan.FromSeconds(1);
var observable = Observable.Interval(period).Publish();
observable.Subscribe(i => Console.WriteLine("first subscription : {0}", i));
Thread.Sleep(period);
observable.Subscribe(i => Console.WriteLine("second subscription : {0}", i));
observable.Connect();
output:
first subscription : 0
second subscription : 0
first subscription : 1
second subscription : 1
first subscription : 2
second subscription : 2
So using Completable Observable, we have a way to control when to let Observable emit items.
Example taken from : http://www.introtorx.com/Content/v1.0.10621.0/14_HotAndColdObservables.html#PublishAndConnect
EDIT
According to 180th slide in this link:
Another nature of publish is that if any observer start observing after 10 seconds of observable started emitting items, observer gets only items those were emitted after 10 seconds(at the time of subscription) not all the items. So in sides, as i could understood that publish is being used for UI events. And it totally makes sense that any observer should only receive those events that has been performed after it has subscribed NOT all the events happened before.
Hope it helps.
I want to asynchronously retrieve data via multiple REST APIs. I'm using Retrofit on Android with the rxJava extension, i.e. I execute any GET request by subscribing to an Observable.
As I said, I have multiple source APIs, so when the first source does not yield the desired result I want to try the next on, if that also fails, again try the next and so forth, until all sources have been queried or a result was found.
I'm struggling to translate this approach into proper use of Observables since I don't know which operators can achieve this behaviour and there are also some constraints to honor:
when a result has been found, the remaining APIs, if any, should not be queried
other components depend on the result of the query, I want them to get an Observable when starting the request, so this Observable can notify them of the completion of the request
I need to keep a reference to aforementioned Observable because the same request could possibly be made more than once before it has finished, in that case I only start it the first time it is wanted and subsequent requests only get the Observable which notifies when the request finished
I was starting out with only one API to query and used the following for the request and subsequent notification of dependent components:
private Observable<String> loadData(int jobId) {
final ConnectableObservable<String> result = Async
.fromCallable(() -> getResult(jobId))
.publish();
getRestRequest()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
dataHolder -> {
if (dataHolder.getData() != null && !dataHolder.getData().isEmpty()) {
saveData(dataHolder.getData());
} else {
markNotFound(dataHolder);
}
},
error -> currentJobs.remove(jobId),
() -> {
currentJobs.remove(jobId);
result.connect();
});
return result;
}
This code was only called for the first request, the returned Observable result would then be saved in currentJobs and subsequent requests would only get the Observable without triggering the request again.
Any help is highly appreciated.
Assuming you have a set of observables that re-connect each time you subscribe:
List<Observable<Result>> suppliers = ...
Then you just need to do the logical thing:
Observable<Result> results = Observable
.from(suppliers)
.concatMap(supplier -> supplier)
.takeFirst(result -> isAcceptable(result))
.cache()
Use .onErrorResumeNext, and assuming that each service observable may return 0 or 1 elements use first to emit an error if no elements are emitted:
Observable<T> a, b, c;
...
a.first().onErrorResumeNext(t -> b.first())
.onErrorResumeNext(t -> c.first())
.onErrorResumeNext(t -> d.first())
...
I have an Observable that can emit events in some time after subscribing (e.g. an Observable from the Retrofit). The subscription to this Observable is strictly View-related, so when the View is destroyed I'm unsubscribing from the Observable. I want to perform some actions in doOnNext even if I unsubscribe the Subscription.
Example code:
final Observable<String> observable = ...; // will emit an event in some time in future
final Subscription subscription =
observable.doOnNext(new Action1<String>() {
#Override
public void call(String s) {
//this should be called even if the subscription is unsubscribed
}
}).subscribe();
subscription.unsubscribe();
Is there a way to make sure doOn<something> will be called even if the Subscription is unsubscribed?
EDIT:
Let me give you a bit clearer example:
final Observable<List<GithubRepo>> observable = getGithubReposFromApi();
subscription = observable
.doOnNext(githubRepos -> cacheGithubReposInDb(githubRepos))
.subscribe(githubRepos -> displayGithubReposInCurrentActivity(githubRepos));
And in Activity's onDestroy:
subscription.unsubscribe();
Now... If githubRepos were received after the Activity has been destroyed, the result wouldn't be cached in the database. And I would like it to be.
Okay, so you want the computation to run but want to cut off the Activity-dependent subscriber. You can publish() the sequence and subscribe to it. When unsubscribed, the original sequence will still go on:
ConnectableObservable<T> co = observable
.doOnNext(githubRepos -> cacheGithubReposInDb(githubRepos))
.publish();
Subscription s = co.subscribe(githubRepos ->
displayGithubReposInCurrentActivity(githubRepos));
co.connect();
// ...
s.unsubscribe();
I think you could do one of two things:
1.) Just have two Subscribers: one for View related stuff and one for the other side-effects that you are currently handling in doOnNext. To still have only one upstream subscription you could use:
Observable sharedObservable = observable.replay().refCount();
or maybe just
Observable sharedObservable = observable.share();
// which is the same as observable.publish().refCount();
2.) Use
Observable observableThatWillNeverTrulyUnsubscribe = observable.doOnNext(/* your doOnNext */).publish().autoConnect();
Now, when your Subscriber subscribes to observableThatWillNeverTrulyUnsubscribe it will subscribe to observable and will start emitting items. However, when the Subscriber unsubscribes, it will not unsubscribe upwards and thus doOnNext will continue to receive items.
I think I would prefer to first option as with the second you are giving up any possibility of ever stopping the work of observable.