How to sew two Observable in RxJava ?
Observable<List<CalendarEvent>>, for each CalendarEvent, I want to do network operation to read the (lat,lon) and fetch place name, and then combine place name back to CalendarEvent.
public Observable<List<CalendarEvent>> getEvents() {
// get events
// translate each Event LatLng to Place and bind it to Event
// return the events
}
public Observable<List<CalendarEvent>> getEvents() {
List<CalendarEvent> sourceList = ...
return Observable.from(sourceList) //emits each item separately
.concatMap(calendarEvent -> applyPlaceName(calendarEvent)) //fetches places and applies them for each item
//fyi: concatMap executes requests sequentially, if you want do it in parallel - use flatMap instead
.toList(); //collects items to list
}
//somewhere in your Networking class
public Observable<CalendarEvent> applyPlaceName(CalendarEvent calendarEvent) {
return Observable ... //do network call and apply placeName on item
}
//p.s. don't forget to apply appropriate Schedulers
No need for something fancy here, this would roughly do what you want I think:
public class Foobar {
void doSomethingWithEvents() {
getEvents().subscribe(new Action1<List<CalendarEvent>>() {
#Override
public void call(List<CalendarEvent> calendarEvents) {
for (CalendarEvent event : calendarEvents) {
getPlaceForEvent(event).subscribe(new Action1<Place>() {
#Override
public void call(Place place) {
event.setPlace(place);
}
});
}
}
});
}
Observable<Place> getPlaceForEvent(CalendarEvent event) {
return Observable.just(new Place());
}
Observable<List<CalendarEvent>> getEvents() {
return Observable.just(new ArrayList<CalendarEvent>());
}
}
Related
I have a chained observable that I created like this:
Disposable disposable = currentUsedAdapter.connect(ip)
.observeOn(AndroidSchedulers.mainThread())
.concatMap(fallbackAdapter(ProtocolType.V2))
.delay(500, TimeUnit.MILLISECONDS)
.concatMap(fallbackAdapter(ProtocolType.V1))
.subscribeWith(connectionSubscriber);
and this is the method fallbackAdapter:
private Function<Boolean, Observable<Boolean>> fallbackAdapter(ProtocolType protocolType) {
return new Function<Boolean, Observable<Boolean>>() {
#Override
public Observable<Boolean> apply(#NonNull Boolean isConnected) throws Exception {
if (isConnected) {
return Observable.just(true);
} else {
TempAdapter adapter = new TempAdapter(context, protocolType);
return currentUsedAdapter.connect(ipAddress);
}
}
};
}
currently this is done staticlly, and it's working fine.
Though I want to create a list of those fallbackAdapter(ProtocolType.*) because I only know the amount of fallbacks during runtime.
So I created this:
ArrayList<Function<Boolean, Observable<Boolean>>> adaptersList = new ArrayList<>();
adaptersList.add(fallbackAdapter(ProtocolType.V2));
...
adaptersList.add(fallbackAdapter(ProtocolType.V9));
Disposable disposable = Observable.fromIterable(adaptersList)
.concatMap(adapter ->
adapter.apply(true))
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(connectionSubscriber);
I have created a list that I can update dynamiclly.
BUT, I am not sure how I can pass the value of isConnected from one adapter to another. I currently pass true to everyone, but some of them should return false, but I'm not sure how I can pass this value from one emitter to another using the Observable.fromIterable.
So my question is how should I change this .concatMap(adapter -> adapter.apply(true)) so that I won't always send true but rather I will send the value that been processed by the previous adapter?
thank you
If it helps anyone...
I didn't find an rxjava way to solve it so I solved in an on old java fashion way...
I have created a builder class and added an observable to my main observable and at the end I returned everything.
something like that:
public class DisposableBuilder {
Observable<Boolean> observable;
public DisposableBuilder() {
}
public void build(String ip) {
observable = currentUsedAdapter.connect(host);
if (adaptersNames != null) {
for (int i = 1; i < adaptersNames.size(); i++) { // skip first adapter (currentUsedAdapter adapter)
this.append(AdapterFactory.getAdapter(context, adaptersNames.get(i)));
}
}
}
public void append(CustomAdapter adapter) {
observable = observable
.delay(200, TimeUnit.MILLISECONDS)
.concatMap(fallbackAdapter(adapter));
}
public Observable<Boolean> getObservable() {
return observable;
}
}
and then I used it like so:
disposableBuilder.build(ip);
this.disposable = disposableBuilder.getObservable()
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(connectionSubscriber);
I'm new to Android development and am currently trying to make a simple MVC app that works with Rest API.
API calls are made without using Retrofit, although this is not so important. The main catch is that using Observable with debounce and SwitchMap I still get too many API calls (and the extra ones should be discarded). The function is called when text is entered (EditText Listener with TextWatcher). And when administered continuously without delay word, every symbol processed by the server and should only be administered when not within 600 milliseconds. Please help me.
public Observable<String> getObservable(final String s){
return Observable
.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext(model.translateText(s));
}
});
}
public Observer<String> observer = new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String s) {
mainView.hideProgress();
mainView.showResult(s);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
};
public void onEditTextChange(String textForTranslate){
mainView.showProgress();
getObservable(textForTranslate)
.debounce(600,TimeUnit.MILLISECONDS)
.switchMap(new Function<String, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(String s) throws Exception {
return Observable.just(s);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
You are creating a new observable every time a character is typed. So multiple observers are created with each having separate debounce (time throttle) and switch but they are not reused. Instead you create a new observable whenever text changes and start rx chain on it.
You need to create a single PublishSubject
private final PublishSubject<String> querySubject = PublishSubject.create();
that emits entered text/query whenever text is changed. Use it in your callback:
public void onEditTextChange(String textForTranslate) {
querySubject.onNext(textForTranslate);
}
And in your main function, subscribe to observable:
querySubject
.debounce(600, TimeUnit.MILLISECONDS)
.distinctUntilChanged()
.switchMap(new Function<String, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(String s) throws Exception {
// perform api call or any other operation here
return Observable.just(s);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
Debounce operator emits single item only after given time (600 ms) has passed. It ignores items if current item is being processed and given time has not passed.
distinctUntilChanged helps in reducing processing of same query.
I'm new to RxJava, I know flatmaps are for mapping an emitted item to observable. I also know that based on the documentation the emitted observables all get combined (flatten) to a single observable stream.
I was wondering what happens if any of those inner observables get completed?
for example: I have an observable that emits a item data key. I have to make another async http call to get the item data from the server, so I call it by using another observable. I use a flat map to connect these two and create one main observable.
When does the run() method of following "SomeMethodThatWantsItems" get called?
public void someMethodThatWantsItems(MyHttpCaller httpCaller, SomeSearchEngine searchEngine)
{
Consumer<Item> onNextConsumer =
Observable<Item> searchObservable = getSearchResult(httpCaller, searchEngine, "The Search Word");
searchObservable
.subscribeOn(Schedulers.newThread())
.subscribe(new Consumer<Item>(){
#Override
public void accept(#NonNull Item item) throws Exception {
//Do stuff with the item
}
}
, new Consumer<Exception>() { //some implementation of onErrorConsumer
}
//OnComplete
, new Action(){
#Override
public void run() throws Exception {
//When does this get called??? after the search complete or when the first http call is successful?
}
});
}
private Observable<String> getSearchResultKeys(SomeSearchEngine searchEngine, String someSearchWord)
{
return Observable.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(#NonNull final ObservableEmitter<String> emitter) throws Exception {
//Assume that our search engine call onFind everytime it finds something
searchEngine.addSearchListener(new searchEngineResultListener(){
#Override
public void onFind(String foundItemKey){
emitter.onNext(foundItemKey);
}
#Override
public void onFinishedFindingResults(){
emitter.onComplete();
}
});
}
});
}
private Observable<Item> getItemByKey(MyHttpCaller httpCaller, String key)
{
return Observable.create(new ObservableOnSubscribe<Item>() {
#Override
public void subscribe(#NonNull final ObservableEmitter<Item> emitter) throws Exception {
//Call the server to get the item
httpCaller.call(key, new onCompleteListener(){
#Override
public void onCompletedCall(Item result)
{
emitter.onNext(result);
//The result is complete! end the stream
emitter.onComplete();
}
});
}
});
}
public Observable<Item> getSearchResult(MyHttpCaller httpCaller, SomeSearchEngine searchEngine, String someSearchWord){
//Where everything comes together
Observable<String> searchResultObservable = getSearchResultKeys(searchEngine, someSearchWord);
retuern searchResultObservable
.observeOn(Schedulers.newThread())
.flatMap(new Function<String, Observable<Item>>(){
#Override
public Observable<Item> apply(String key){
return getItemByKey(httpCaller, key);
}
});
}
The onComplete() always get call once and then the streams stops. (this is part of the Observable Contract).
That means that in your case, your onComplete() at SomeMethodThatWantsItems will be called after all items were retrieved.
In case of flatMap(), completion of each inner Observable, simply will signal the source Observable to stop flatting item from the inner Observable to the source Observable, flatMap() merges items from the inner Observable as long as this stream sends items, so it's basically consume the entire inner Observable stream into the source stream, the entire stream is until termination event3 like onComplete(), so in case where inner Observable can emit more than 1 item, that means that it will make more than 1 emission on the source stream.
I'm new in RxJava so I still have many doubts.
I'm creating this:
#Override
public Single<Result> saveUser(final User user) {
return Single.create(new Single.OnSubscribe<Result>() {
#Override
public void call(final SingleSubscriber<? super Result> singleSubscriber) {
if(user.isValid()){
save(user);
//Set result ok
singleSubscriber.onSuccess(result);
} else {
//This sets an error.
singleSubscriber.onError(error);
}
}
});
}
Depending of the success of the operation, the Single emits it's data and who is subscribed receives the data.
The problem now is that at some point I need to store a list of users. Something like:
public void saveUsers(List<User> listOfUsers){
for (User user : listOfUsers) {
saveUser(user);
}
}
How can I create an Single so I can be subscribed to the initial Single
I would rather create flatmap out of observable list.
public void saveUsers(List<User> listOfUsers){
Observable.from(listOfUsers).flatMap((User user)->{
if(user.isValid()){
save(user);
//Set result ok
return Observable.just(result);
}else
return Observable.error(new RuntimeException("..."));
}).retry(2);
}
If you make your saveUsers method blocking, call Observable#toBlocking.
I'm using RxJava and nesting Observables as below. I want to call one observable inside another observable and have the outer observable emit the result of the inner observable onNext. It appears to work, but I'm not certain this is the correct implementation since I wasn't able to find any documentation to confirm.
public Observable<User> updateUser(final String id) {
return Observable.create(new Observable.OnSubscribe<User>() {
#Override
public void call(final Subscriber<? super User> observer) {
try {
if (!observer.isUnsubscribed()) {
getUser(id).subscribe(new Action1<User>() {
#Override
public void call(User user) {
observer.onNext(user);
observer.onCompleted();
}
});
}
} catch (Exception e) {
observer.onError(e);
}
}
});
}
public Observable<User> getUser(final String id) {
...
}
Avoid calls to subscribe when you are just doing Observable transformations as you have to be mindful of all the problems mentioned by #akarnokd and #zsxwing in the comments.
I would also avoid using Observable.create because creating OnSubscribe implementations involves considering backpressure and consequent tricky business with concurrency. Prefer Observable.just,Observable.from,Observable.range,Observable.defer, Observable.using (there are more, check the wiki) and for more advanced purposes implement SyncOnSubscribe.
This code probably covers your use case:
public Observable<User> updateUser(final String id) {
return getUser(id).doOnNext(user -> updateUser(user));
}
public void updateUser(User user) {
//whatever you want here
}
public Observable<User> getUser(final String id) {
...
}