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)
Related
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.
I'm new to RxJava. My requirement is to do 3 retrofit call at start and wait until all of theme executed. Here is what i had implemented and Its working perfectly but I want know, is this code could be better than this and I implemented schedulers correctly or not.
public class CombinedGroupProductPage {
private List<Product> groupProductList;
private List<Product> relatedProductList;
private List<Product> upsellProductList;
//constructor and getter setters here
.....
.....
}
Here is my implementation
private void getAllData() {
loadingProgress.setVisibility(View.VISIBLE);
ApiInterface apiService = ApiClient.getRxClient().create(ApiInterface.class);
Observable<List<Product>> call = apiService.getRelatedProduct1(gson.toJson(model.getGroupedProducts()))
.subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.io());
Observable<List<Product>> call1 = apiService.getRelatedProduct1(gson.toJson(model.getRelatedIds()))
.subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.io());
Observable<List<Product>> call2 = apiService.getRelatedProduct1(gson.toJson(model.getUpsellIds()))
.subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.io());
Observable<CombinedGroupProductPage> combined = Observable.zip(call, call1, call2, new Function3<List<Product>, List<Product>, List<Product>, CombinedGroupProductPage>() {
#Override
public CombinedGroupProductPage apply(List<Product> list, List<Product> list2, List<Product> list3) throws Exception {
return new CombinedGroupProductPage(list, list2, list3);
}
}).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread());
combined.subscribe(new Observer<CombinedGroupProductPage>() {
#Override
public void onSubscribe(Disposable d) {
// loadingProgress.setVisibility(View.VISIBLE);
}
#Override
public void onNext(CombinedGroupProductPage combinedGroupProductPage) {
Log.e("Tag", combinedGroupProductPage.toString());
Log.e("Tag", combinedGroupProductPage.getGroupProductList().get(0).getName());
loadingProgress.setVisibility(View.GONE);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
}
Please tell me this code can be reduced or not? Any help would be appreciated thanks.
Use Schedulers.io() instead of Schedulers.newThread(). Schedulers.io() uses a thread pool whereas Schedulers.newThread() doesn't. Creating threads is expensive and should be avoided if possible.
Using .subscribeOn(Schedulers.io()) for your different calls also allow you to remove the now useless .observeOn(Schedulers.io()).
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'm new into rxJava and it's making my head spin. Basically I'm pulling data from youtube api with retrofit which gives back Observable and with youtubeDataMapper I'm mappng it into Youtube Pojo object which contains String videoID. So my question is, how to make this method return that string instead of Completable?
This is my method:
#Override
public Completable downloadVideoUrl(String query) {
addSubscription(youtubeApi.getYoutubeId(query, Constants.youtubeApi)
.map(youtubeDataMapper::map)
.subscribeOn(subscribeScheduler)
.observeOn(observeScheduler)
.subscribe());
return Completable.complete();
}
You have two choices:
Make your downloadVideoUrl return Observable instead of Completable:
Preferred way:
#Override
public Completable downloadVideoUrl(String query) {
return youtubeApi.getYoutubeId(query, Constants.youtubeApi)
.map(youtubeDataMapper::map)
.subscribeOn(subscribeScheduler)
.observeOn(observeScheduler);
}
Notice lack of subscribe operator here.
Then wherever you want to get videoId:
downloadVideoUrl(query)
.subscribe(new Subscriber<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String videoId) {
// do whatever you want with videoId
}
});
Use toBlocking().first()
This is not preffered as you block current Thread until Observable finishes
#Override
public String downloadVideoUrl(String query) {
return youtubeApi.getYoutubeId(query, Constants.youtubeApi)
.map(youtubeDataMapper::map)
.subscribeOn(subscribeScheduler)
.observeOn(observeScheduler)
.toBlocking().first();
}
First of all, it is better to make Retrofit return Single instead of Observable because you are expecting a single server response (and not a sequence of responses).
Secondly, Completable.complete() is a factory method for a Completable that does nothing at all. So you don’t need it here.
Regarding String videoID, it depends on what you are planning to do with it. Also, I have no idea what your .addSubscription() is doing.
I would suggest doing something like the following:
class YourClass {
private final CompositeSubscription compositeSubscription = new CompositeSubscription();
// you must call compositeSubscription.clear() either in class .finalize() or on some UI lifecycle event
void yourMethod() {
final Single videoID = youtubeApi.getYoutubeId(query, Constants.youtubeApi)
.map(youtubeDataMapper::map)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
final Subscription subscription = videoID.subscribe(new SingleSubscriber() {
#Override
public void onSuccess(String value) {
// TODO: do whatever with the value
}
#Override
public void onError(Throwable error) {
// TODO: log and/or display error
}
});
compositeSubscription.add(subscription);
}
}
So this is what I have so far...
public Observable<List<Integer>> getIds() {
return Observable.create(new Observable.OnSubscribe<List<Integer>>() {
#Override
public void call(Subscriber<? super List<Integer>> subscriber) {
try {
subscriber.onNext(mSource.getIds());
} catch (Exception e) {
subscriber.onError(e);
}
}
});
}
This works fine and gets me the list of ids for the objects I'm trying to create
Then I need a function that returns a subscription to an Observer with a list of objects. I need to make a separate api call to getObject(int id) to get each of these objects.
public Subscription getObjectList(Observer<List<Object>> observer) {
return mService.getIds()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
// here is where i get lost...
.map(new Func1<Observable<Integer>, Observable<List>>)
Any help would be appreciated thanks.
Alright so since your getIds() method returns an Observable<List<Integer>> the proper way to create your Func1 in map would be
.map(new Func1<List<Integer>, List<Object>>() {
It takes in a list of integers and will emit a list of objects. I believe that is what you're trying to achieve. Your final code could look something like so
public Subscription getObjectList(Observer<List<Object>> observer) {
return getIds()
.map(new Func1<List<Integer>, List<Object>>() {
#Override
public List<Object> call(List<Integer> integers) {
List<Object> objects = new ArrayList<Object>();
for (Integer id : integers) {
// Here's where you map each ids to an object and add it to the list
objects.add(getObject(id));
}
return objects;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
// Add your own code here to load the object based on the id.
private Object getObject(Integer id) {
return id;
}
// Replace with your own getIds from above
public Observable<List<Integer>> getIds() {
return Observable.from(1, 2, 3).toList();
}
This should help you get started, let me know if there's something which you don't understand.