How do I properly implement Observable.zip with Bifunction?
One returns a Single, one returns an Observable, not sure if this possible.
Thanks for the comment this is my updated. I'm not sure what to put in the BiFunction below
Observable.zip(
mCurrentUserViewModel.getUserProfile(getUserProfileRequest).toObservable(),
mTopicsByCurrentUserViewModel.getTopicsByCreator(getTopicsByCreatorRequest)),
new BiFunction<Single<GetUserProfileResponseBody>, Observable<Response<GetTopicsByCreatorResponseBody>>, **what do i put here**>()
);
you have two options to do so.
toSingle() converts an Observable which emits a single item to a Single that emits this item
toObservable converts a Single into an Observable.
for example : using toObservable()
1-create your observables
private Observable<String> getObservable() {
return Observable.just("observable");
}
private Single<String> singleObservable() {
return Single.just("single");
}
private Observable<String> mergedObservable() {
return Observable.zip(getObservable(), singleObservable().toObservable(), new BiFunction<String, String, String>() { //the result of merging is also String
#Override
public String apply(String s, String s2) {
return s + s2; //or if you are using different objects create a model AandB. where A and B are the result of a seperate observers.
}
});
}
2-merge them
mergedObservable().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String s) {
Log.d(tag,s);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
OUTPUT:
observablesingle
an example zip function :
private Observable<String> stringObservable() {
return Observable.just("D");
}
private Observable<Integer> integerObservable() {
return Observable.just(1);
}
private Observable<String> resultObservable(){
return Observable.zip(stringObservable(), integerObservable(), new BiFunction<String, Integer, String>() {
#Override public String apply(String s, Integer integer) throws Exception {
return s + integer;
}
});
}
private Observable<String> resultObservableLambda(){
return Observable.zip(stringObservable(), integerObservable(), (s, i) -> s + i);
}
The 3rd item you are missing is your return type. So what is the aggregated type you want to return - in this example String, so a new Observable Observable<String> is returned.
Related
I'm just starting out with RxJava and trying a sample project.
What I'm trying to achieve is
-> Get an object ->
which contains a list of sub-objects -> Check if the sub-list satisfies a predicate condition -> and emit the sub-objects if satisfies
This is my POJO
public class UpComingMovies {
#SerializedName("results")
private List<Movies> results;
}
public class Movies {
#SerializedName("overview")
private String overview;
#SerializedName("original_language")
private String originalLanguage;
}
So, from what I understand is that I can use flatMap and transform the item to multiple observables and then use filter saying give me movies which has originalLanguage.equals("en")
This is what I have tried to do
#GET("movie/upcoming")
Observable<UpComingMovies> getUpComingMovies(#Query("api_key") String apiKey, #Query("page") String page);
private final CompositeDisposable disposables = new CompositeDisposable();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
disposables.add(RetrofitConnection.getService()
.getUpComingMovies(Config.API_KEY, "1")
.flatMapIterable(new Function<UpComingMovies, Iterable<Movies>>() {
#Override
public Iterable<Movies> apply(#NonNull UpComingMovies upComingMovies) throws Exception {
// does not compile - needs an iterable
return upComingMovies.getResults().iterator();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<UpComingMovies>() {
#Override
public void onNext(UpComingMovies upComingMovies) {
Log.d(getClass().getName(), upComingMovies.toString());
}
#Override
public void onError(Throwable e) {
Log.d(getClass().getName(), e.getMessage());
}
#Override
public void onComplete() {
Log.d(getClass().getSimpleName(), "onComplete");
}
}));
}
However, it does not compile.
Cleary I do not know how to do this, any help is appreciated
Your filter shouldn't compile because of the previous function that generates an iterable of UpcomingMovies. In other words, that filter is expecting an observable of movies, but the previous function is mapping to an iterable of UpcomingMovies
You seem to be wanting to map UpcomingMovies into a List / Iterable of Movies
In order to do that, try starting with this Function definition
flatMapIterable(new Function<UpComingMovies, Iterable<Movies>>()
Then, you can use getResults() of the UpcomingMovies object parameter, to get the sublist, return it, then that's passed to the subsequent filter operation
In code,
.flatMapIterable(new Function<UpComingMovies, Iterable<Movies>>() {
#Override
public Iterable<Movies> call(UpComingMovies upcomingMovies) {
return upcomingMovies.getResults();
}
})
Also, if you enable Java 8 compilation with lambdas, then the code is much simpler... Reduced to one line
.flatMapIterable(UpComingMovies::getResults)
I'm just starting out with RxJava and trying a sample project.
What I'm trying to achieve is
-> Get an object ->
which contains a list of sub-objects -> Check if the sub-list satisfies a predicate condition -> and emit the sub-objects if satisfies
This is my POJO
public class UpComingMovies {
#SerializedName("results")
private List<Movies> results;
}
public class Movies {
#SerializedName("overview")
private String overview;
#SerializedName("original_language")
private String originalLanguage;
}
So, from what I understand is that I can use flatMapIterable and transform the item to multiple observables and then use filter saying give me movies which has originalLanguage.equals("en")
This is what I have tried to do
#GET("movie/upcoming")
Observable<UpComingMovies> getUpComingMovies(#Query("api_key") String apiKey, #Query("page") String page);
private final CompositeDisposable disposables = new CompositeDisposable();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
disposables.add(RetrofitConnection.getService()
.getUpComingMovies(Config.API_KEY, "1")
.flatMapIterable(new Function<UpComingMovies, Iterable<Movies>>() {
#Override
public Iterable<Movies> apply(#NonNull UpComingMovies upComingMovies) throws Exception {
// no instance(s) of the type variable(s) U exist so that the Observable<U> conforms to a disposable
return upComingMovies.getResults();
}
})
.filter(movies -> movies.getVoteCount() > 200).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new DisposableObserver<Movies>() {
#Override
public void onNext(#NonNull Movies movies) {
}
#Override
public void onError(#NonNull Throwable e) {
}
#Override
public void onComplete() {
}
}));
}
However, it does not compile.
Cleary I do not know how to do this, any help is appreciated
The reason it doesn't compile is because you're returning an observable and your function signature is expecting an iterable:
new Function<UpComingMovies, Iterable<Movies>>() {
#Override
public Iterable<Movies> apply(#NonNull UpComingMovies upComingMovies) throws Exception {
// List<Movies> is iterable
return upComingMovies.getResults();
}
}
If you fix it this way, then you can do
...flatMapIterable(...)
.filter(new Predicate<Movies>() {
#Override
public boolean test(Movies movies) throws Exception {
return movies.originalLanguage.equals("en");
}
})...
I don't know if it is exactly what you're trying to do but replace
return Observable.fromIterable(upComingMovies.getResults());
with
return upComingMovies.getResults();
if you want your project to compile
If I understood your code correctly, you get a single result of type UpcomingMovies (which is why you should use Single here and not Observable) and then flatten that to multiple emissions of type Movies. That's why your subscribe function doesn't work: it expects UpcomingMovies but gets Movies.
Something like this is probably what you want:
#GET("movie/upcoming")
Single<UpComingMovies> getUpComingMovies(#Query("api_key") String apiKey, #Query("page") String page);
RetrofitConnection.getService()
.getUpComingMovies(Config.API_KEY, "1")
.flatMapIterable((UpComingMovies upComingMovies) -> {
return upComingMovies.getResults();
})
.filter((Movies movie) -> {
return movie.originalLanguage.equals("en");
})
.subscribe((Movies movie) -> {
Log.d("", movie.toString());
});
I have the following code:
/**
* Request wrapped around flowable.
*/
public abstract class RequestFlowable<T> {
private final PublishProcessor<String> mPublish;
private String mName;
public RequestFlowable(String name) {
mName = name;
mPublish = PublishProcessor.create();
}
public Flowable<T> getFlowable() {
//return createAction();
return mPublish.compose(new FlowableTransformer<String, T>() {
#Override
public Publisher<T> apply(#NonNull Flowable<String> upstream) {
return createAction();
}
});
/*
return mPublish.flatMap(new Function<String, Publisher<? extends T>>() {
#Override
public Publisher<? extends T> apply(#NonNull String s) throws Exception {
return createAction();
}
});
*/
}
protected abstract Flowable<T> createAction();
public String getName() {
return mName;
}
public void start() {
mPublish.onNext("processCommand");
}
#Override
public String toString() {
return "Request: " + mName;
}
}
Now for Single
#EDIT 2
public abstract class Request<T> {
private final SingleSubject<Object> mPublish;
private String mName;
public Request(String name) {
mName = name;
mPublish = SingleSubject.create();
}
public Single<T> getSingle() {
return mPublish.flatMap(o -> createAction());
}
protected abstract Single<? extends T> createAction();
public String getName() {
return mName;
}
public void start() {
mPublish.onSuccess("Start");
}
#Override
public String toString() {
return "Request: " + mName;
}
}
The code from the above works when used with compose, like in code from above but, if instead I put the commented code - aka flatMap for some reason createAction is not executed.
EDIT 2
The code from the above is called from another class. The corresponding code is attached below(important parts of class added):
public class RequestQueue implements RequestController {
private static final String TAG = RequestQueue.class.getSimpleName();
private PublishSubject<Request> mRequest;
private PublishSubject<RequestFlowable> mRequestFlowable;
#Override
public <T> Single<T> registerRequest(Request<T> request) {
mRequest.onNext(request);
return request.getSingle();
}
#Override
public <T> Flowable<T> registerRequestFlowable(RequestFlowable<T> request) {
mRequestFlowable.onNext(request);
return request.getFlowable();
}
public RequestQueue() {
mRequest = PublishSubject.create();
mRequestFlowable = PublishSubject.create();
mRequest.subscribe(this::actionOnRequest);
mRequestFlowable.subscribe(this::actionOnRequest);
}
private void actionOnRequest(Request request) {
Log.d(TAG, "actionOnRequest() called with: request = [" + request + "]");
request.start();
}
private void actionOnRequest(RequestFlowable request) {
Log.d(TAG, "actionOnRequest() called with: request = [" + request + "]");
request.start();
}
}
(From my comments:)
Why does Single work?
SingleSubject retains the single terminal event it received. Since it can only receive onSuccess and onError, it will "replay" that to late subscribers (also this is why there is no separater ReplaySingleSubject). When you call onSuccess on the SingleSubject, that value is remembered and promplty reemitted when the later subscription happens, calling your createAction. PublishProcessor also remembers its terminal events but onNext is not a terminal event, hence dropped without consumer.
How can the desired behavior be achieved via Processor?
You could reorganize your logic, use BehaviorProcessor or ReplayProcessor.createWithSize(1). Calling onComplete won't execute the flatMap function either.
Following this tutorial I've created two sources for fetching data. I expect that if there is no data locally I'll send network request. But all the time get list of null objects from local source (which is first in Observable.concat).
For local source using SQLite with SQLBrite wrapper and Retrofit for remote source:
#Override
public Observable<Item> get(String id) {
//creating sql query
return databaseHelper.createQuery(ItemEntry.TABLE_NAME, sqlQuery, id)
.mapToOneOrDefault(mapperFunction, null);
}
There is method in repository for concating observables:
#Override
public Observable<Item> get(String id) {
Observable<Item> local = localDataSource
.get(id)
.doOnNext(new Action1<Item>() {
#Override
public void call(final Item item) {
// put item to cache
}
});
Observable<Item> remote = remoteDataSource
.get(id)
.doOnNext(new Action1<Item>() {
#Override
public void call(final Item item) {
// save item to database and put to cache
}
});
return Observable.concat(local, remote).first();
}
For getting it with list of ids I'm using next method:
#Override
public Observable<List<Item>> getList(final List<String> ids) {
return Observable
.from(ids)
.flatMap(new Func1<String, Observable<Item>>() {
#Override
public Observable<Item> call(String id) {
return get(id);
}
}).toList();
}
And subscription in fragment:
Subscription subscription = repository
.getList(ids)
.flatMap(new Func1<List<Item>, Observable<Item>>() {
#Override
public Observable<Item> call(List<Item> result) {
return Observable.from(result);
}
})
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Item>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Item> result) {
// there get list of null objects
}
});
So, main goal is first check local storage and if there is no item - make request to server. But now if item isn't exist I get null instead of send request.
Could someone help me understand why?
Calling first() after concat will of course return the first item, regardless if it's valid.
Yout first() function should validate the value and only submit a 'valid' first item.
Something like:
public void test() {
Integer[] ids = new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
Observable.from(ids)
.flatMap(new Func1<Integer, Observable<String>>() {
#Override
public Observable<String> call(Integer id) {
return get(id);
}
})
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<String>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<String> strings) {
}
});
}
public Observable<String> get(int id) {
return Observable.concat(getLocal(id), getRemote(id))
.first(new Func1<String, Boolean>() {
#Override
public Boolean call(String s) {
return s != null;
}
});
}
public Observable<String> getLocal(int id) {
return Observable.just(id < 5 ? "fromLocal id:"+id : null);
}
public Observable<String> getRemote(int id) {
return Observable.just(id >= 5 ? "fromRemote id:"+id : null);
}
Will bring u the result:
fromLocal id:1
fromLocal id:2
fromLocal id:3
fromLocal id:4
fromRemote id:5
fromRemote id:6
fromRemote id:7
fromRemote id:8
fromRemote id:9
fromRemote id:10
Answer from github:
Your function Observable get() returns endless Observable because
mapToOneOrDefault does not complete Observable on its own and reacts to
updates of the db. You need to limit emission of this Observable because
operator concat waits for onCompleted event.
For example, it should looks like:
return Observable.concat(localSource.first(), remoteSource)
.filter(new Func1<Item, Boolean>() {
#Override
public Boolean call(Item item) {
return item!= null;
}
});
I have to execute 3 API calls in the sequence and to do so
I use observable.concatMap(new Func1<>...)
and at the last one I have a subscriber to change activity
However I want to update progressBar in UI thread to let user know that part of task is done.
private void getAllData() {
updateUserTask(
getUserScheduleObservable(
getCurrentUserObservable()));
}
private void updateUserTask(Observable<TaskWrapper> observable) {
wrapObservable(observable)
.subscribe(new Subscriber<TaskWrapper>() {
#Override
public void onNext(TaskWrapper taskWrapper) {
openCurrentFragment();
hideProgressIndicators();
}
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable throwable) {
}
});
}
private Observable<TaskWrapper> getUserScheduleObservable(Observable<ScheduleWrapper> observable) {
return observable.concatMap(
scheduleWrappers1 -> apiManager.getRouteObservable(vehicleDeliveryAreaRiderBundle.getVehicle().getId()));
}
private Observable<ScheduleWrapper> getCurrentUserObservable() {
return apiManager.getUserObservable().concatMap(
user -> apiManager.getCurrentScheduleObservable()
);
}
I think that you are looking for something like this.
public class ExampleUnitTest {
#Test
public void testSample() throws Exception {
Observable<String> first = Observable.just("First");
Observable<String> second = Observable.just("Second");
Observable<String> third = Observable.just("Third");
Observable.concat(first, second, third)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(this::updateProgress)
.subscribe();
}
private void updateProgress(String s) {
System.out.println(String.format("Notify your progress that %s ended", s));
}
}
Just concatenating those observables, you can achieve the expected result.
Hope that it helps.
Best regards.