I have a BehaviorSubject data that contains actual data (or maybe nothing, if nothing has been emitted to it). I want to subscribe for only one item it emits, i.e. either the current observed value or the first to be passed to it from somewhere else. I'm currently doing it the following way:
Subscription firstItemSubscription = data.subscribe(item -> {
firstItemSubscription.unsubscribe();
processItem(item);
});
Is there any operator or transformer that I could use instead? Or probably there is completely different, more Rx approach that would allow me to do the thing I want to?
Yes use just need to use take(1)
Observable observable = //some observable
observable.take(1).subscribe(/* do your thing */);
Related
I have a retrofit api call that returns a javarx observable. Here is the retrofit interface
#GET("/myobjects")
Observable<List<MyObject>> getMyObjectsObservable();
Then I have a repository method:
public Observable<List<MyObject>> getMyObjectsObservable(){
return api.getMyObjectsObservable();
}
In my viewmodel, I have a method that subscribes to the javarx observable returned by the repository. Inside the method, the observable chain in which I subscribe in that method sets values for two MutableLiveData objects that are observed by my activity. One MutableLiveData, "succeededLiveData", holds a boolean which determines if the API call succeeded and displays this for the user in the activity, and the other "myObjectStringsListLiveData" is a list of strings with information from a MyObject object that will be displayed in a listview in the activity. Here is that observable chain:
Disposable disposable = myObjectsRepository.getMyObjectsObservable()
.concatMapIterable(myObject -> myObject)
.map(myObject -> myObject.getString1() + " And " + myObject.getString2())
.toList()
.doOnError(throwable -> {succeededLiveData.postValue(false);
myObjectStringsListLiveData.postValue(null);})
.retry(4)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(myObjectStringsList -> {
succeededLiveData.setValue(true);
myObjectStringsListLiveData.setValue(myObjectStringsList);
},
throwable -> {
succeededLiveData.setValue(false);
myObjectStringsListLiveData.setValue(null);});
Now I realized that I actually need the original list of my MyObjects from the observable to be stored in List<MyObject> myObjectsList which is a field of my viewmodel, not just the List<String> that is stored in my LiveData "myObjectStringsListLiveData". This means that I need a way to extract the myObjectsList from the observable before all the mapping. I can't observe the myObjectsObservable twice, once before the mapping, and then one mapped like my code above, because that would cause two API calls which I obviously do not want. At least it is my understanding that that would happen, as observables execute the code separately for each subscriber.
So I can only personally think of two possible solutions as I am still a javarx noob. One would be to use a type of Subject that subscribes to the retrofit myObjectsObservable so that the API wouldn't be called twice and then subsequently subscribe to that subject twice in my viewmodel. Although I am not quite sure how to implement this, but I think I could figure it out.
The other option I can think of would be to subscribe only once, and inside the lambda I pass the subscribe() method, store the List<MyObject emitted by the Observable, and then instead of using the JavaRx map/concatMapIterable/toList() operators, use the java Stream library inside the lambda as well, and use that to map the myObjectsList to List<String> that will be posted to the myObjectStringsListLiveData. A downside to this would that this would occur on the main thread I believe. Although maybe I could change the "setValue" methods to "postValue" methods and observeOn a background thread. I am not really sure if that is okay to use the viewmodel in the background like that.
The second solution doesn't seem too elegant. Is it wise to do stuff with a viewmodel in the background? Maybe the first way with a Subject is better?
What do you all think is the best way?
Also, as a bonus question, that is somewhat unrelated to the first question, I was wondering if postValue in MutableLiveData will always execute in order, like how I do so in my lambdas in the observable chain, because I will get a NullPointerException in my activity if succeededLiveData.getValue() is called after OnChanged is called for the MyObjectStringsList that I observe in my activity. Will postValue always propagate to each respective LiveData observer in the order they are called?
Thanks for any responses!
In RxJava2, we have Single as an Observable that will only generate one and only one data.
One could code
Single.just(...)
// OR
Single.fromCallable{ ... }
However, in Observable we could also do that
Observable.just(...)
// OR
Observable.fromCallable{ ... }
The different is in Observable, we'll have an extra onComplete callback once the one and only data is given. Nothing else.
Hence I was wondering do we ever need Observable.just(..) or Observable.fromCallable{...} since the Single version of it should suffice? Is there a scenario where Observable.just(..) or Observable.fromCallable{...} can achieve (or better than) but not with Single.just(..) or Single.fromCallable{...}?
In Reactive Java, we're told that the .subscribe() call returns "a Subscription reference". But Subscription is an interface, not a class. So what kind of object are we handed that implements this interface? Do we have any control over this?
There is the class Subscriptions that can create and return several different kinds of Subscription, but what does one do with them? If I write
Subscription mSub = Subscriptions.create(<some Action0>);
mSub = someObservable.subscribe();
won't my just-created Subscription simply be overwritten by whatever the .subscribe() call returns? How do you use a Subscription you create?
(On a somewhat related note, what is the point of Subscriptions.unsubscribed(), which "returns a Subscription to which unsubscribe does nothing, as it is already unsubscribed. Huh?)
Short answer: You shouldn't care.
Longer answer: a subscription gives you two methods:
unsubscribe(), which causes the subscription to terminate.
isUnsubscribed(), which checks whether that has already happened.
You can use these methods to a) check whether an Observable chain terminated and b) to cause it to terminate prematurely, for example if the user switched to a different Activity.
That's it. You aren't exposed to the internals on purpose. Also, do you notice that there's no resubscribe method? That's because if you want to restart the operation, you need to resubscribe to the Observable, giving you a new Subscription.
As you know Subscriptions are used to keep references to ongoing Observables, mainly for resources' management. For example in Android applications, when you change an Activity (screen) you flush old Activity Observables. In this scenario, Subscription instances are given by .subscribe() (as you mentioned) and stored. So, for which reason would one create a Subscription directly, especially Subscriptions.unsubscribed()? I encountered two cases:
Default implementation; avoid declaration like Subscription mSub; that would be filled latter and could create an NPE. It's especially true if you use Kotlin that require property initialization.
Testing
On a somewhat related note, what is the point of Subscriptions.unsubscribed(), which "returns a Subscription to which unsubscribe does nothing, as it is already unsubscribed. Huh?
In 1.x, Subscriptions.unsubscribed() is used to return a Subscription instance the operation was completed (or never run in the first place) when the control is returned to your code from RxJava. Since being unsubscribed is stateless and a constant state, the returned Subscription is a singleton because just by looking at the interface Subscription there is no (reasonable) way to distinguish one completed/unsubscribed Subscription from another.
In 2.x, there is a public and internal version of its equivalent interface, Disposable. The internal version is employed mostly to swap out a live Disposable with a terminated one, avoiding NullPointerException and null checks in general and to help the GC somewhat.
what does one do with them?
Usually you don't need to worry about Subscriptions.create(); it is provided for the case you have a resource you'd like to attach to the lifecycle of your end-subscriber:
FileReader file = new FileReader ("file.txt");
readLines(file)
.map(line -> line.length())
.reduce(0, (a, b) -> a + b)
.subscribe(new Subscriber<Integer>() {
{
add(Subscriptions.create(() -> {
Closeables.closeSilently(file); // utility from Guava
});
}
#Override public void onNext(Integer) {
// process
}
// onError(), onCompleted()
});
This example, demonstrating one way of usage, can be expressed via using instead nonetheless:
Observable.using(
() -> new FileReader("file.txt"), // + try { } catch { }
file -> readLines(file).map(...).reduce(...),
file -> Closeables.closeSilently(file)
)
.subscribe(...)
I'm using a Builder pattern to build upon a model object which combines data from different network calls and I'm having a hard time understanding the best way to take the model object from the first network call and combine the data from the second network call into the original model object.
My actual subscription:
myFirstApiRepository.getFirstModelObjectBuilder()
.flatmap(firstModelObjectBuilder -> mySecondApiRepository.getSomeExtraData(firstModelObjectBuilder))
.observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getMySubscriber());
First network call:
public Observable<FirstModelObject.Builder> getFirstModelObjectBuilder() {
return myFirstApiResource.getSomeData(...)
.flatMap(someData -> Observable.just(new FirstModelObject.Builder()
.setFirstAttribute(someData.getFirstAttribute())
.setSecondAttribute(someData.getSecondAttribute())));
}
Second network call:
public Observable<FirstModelObject> getSomeExtraData(FirstModelObject.Builder builder) {
return mySecondApiResource.getSomeData(...)
.flatMap(aString -> builder.setSomeStringValue(aString)
.build());
}
The problem here is that I have to pass the builder object into the second network call's observable. This makes it very rigid and I'd rather not have my SecondApiRepository rely on and reference a data type which it shouldn't need to reference. It also makes this second method here ".build()" the object, which is not good. So, how can I use the firstModelObject and add data to it from the second network call in a clean way?
If this is just bad design, please let me know. I'm still trying to learn more about RxJava best practices. :)
If your second request relies on first request's result, then check my answer - https://stackoverflow.com/a/41820372/7045114.
If not - just use zip operator:
Combines the emissions of multiple Observables together via a specified function and emit single items for each combination based on the results of this function.
Observable.zip(firstRequest, secondRequest, (firstResult, secondResult) -> {
//process results
})
Writing in Java I call zip() method that receives a few method that return an Observable<...>.
Currently I am not able to progress to the following map and this is probably due to the fact that one of the methods didn't return a value yet. (Though it seems all methods where called.)
Is there a way to debug the process and see why it is stuck?
Thanks.
Suppose you have:
result = Observable.zip(sourceA, sourceB, sourceC)
Just add a .doOnNext() on each of the sources to log what they are emitting (or instead of doOnNext, subscribe to each). For instance:
result = Observable.zip(sourceA.doOnNext(/*logging...*/),
sourceB.doOnNext(/*logging...*/),
sourceC.doOnNext(/*logging...*/))
What's probably happening is that one of these sources isn't emitting at the same frequency as the others. zip must be used when you strictly know that all the sources emit events at the same pace/frequency. You might want to try using combineLatest. The difference between the two are:
zip: the returned Observable emits the n-th 'combination' item only when all the n-th items of the sources have been emitted. See a diagram.
combineLatest: the returned Observable emits an 'combination' item whenever any of its sources emits an item. See a diagram.