I have two request of api
// to get all products list inside specific category
1. getCategoryProducts : Observable<List<CategoryProductsModel>
class CategoryProductsModel {
int categoryId ;
String sku ;
// sku is variable I used it to get the details for each product (name , images , colors , sizes , price ..etc
}
// to get product details by sku based on first api getCategoryProducts
2.getProductDetails(var sku:String): Observable<ProductDetailsModel>
class ProductDetailsModel {
float price ;
List<Images> images ,
List<Colors> colors ,
etc ...
}
first request is getCategoryProducts then getProductDetails based on the first request to get the sku for each product and pass it to getProductDetails
after I make the second request I want to add the data response in ProductModel to get finally a list of ProductModel to pass it to recycler view
ProductModel
{
CategoryProductsModel mCategoryProductsModel ;
ProductDetailsModel mProductDetailsModel ;
}
I user RxJava to request API. How can I do that?
public void getProductsList() {
getCategoryProductsObservable()
.subscribeOn(Schedulers.io())
.flatMap(new Function<CategoryProductsModel, ObservableSource<List<ProductModel>>>() {
#Override
public ObservableSource<List<ProductModel>> apply(CategoryProductsModel mCategoryProductsModel) throws Exception {
return getProductDetails(mCategoryProductsModel);
}
}).observeOn(AndroidSchedulers.mainThread())
.subscribe(this::fillProducts);
}
public Observable<CategoryProductsModel> getCategoryProductsObservable() {
return Repository.Companion.getStoreInstance().getCategoryProducts(categoryId).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Function<List<CategoryProductsModel>, ObservableSource<CategoryProductsModel>>() {
#Override
public ObservableSource<CategoryProductsModel> apply(List<CategoryProductsModel> mCategoryProductsList) throws Exception {
return Observable.fromIterable(mCategoryProductsList).subscribeOn(Schedulers.io());
}
}).subscribeOn(Schedulers.io());
}
#SuppressLint("CheckResult")
public Observable<List<ProductModel>> getProductDetails(CategoryProductsModel mCategoryProducts) {
List<ProductModel> mList = new ArrayList<>() ;
return Repository.Companion.getStoreInstance().getProductDetails(mCategoryProducts.getSku())
.map(new Function<ProductDetailsModel, List<ProductModel>>() {
#Override
public List<ProductModel> apply(ProductDetailsModel mProductDetailsModel) throws Exception {
Log.d("mProductDetailsModel", mProductDetailsModel.getName());
ProductModel productModel = new ProductModel() ;
productModel.setProductDetailsModel(mProductDetailsModel);
productModel.setCategoryProductsModel(mCategoryProducts);
mList.add(productModel);
return mList;
}
}).subscribeOn(Schedulers.io());
list of operators you need: switchMap() fromIterable() and toList(). So your code will look like:
getCategoryProductsObservable()
.subscribeOn(Schedulers.io())
.switchMap { listOfProducts -> Observable.fromIterable(listOfProducts)
.map { it.sku }
.flatMap { sku -> getProductDetails(sku) }
.toList()
.map { listOfProductDetails -> /* combine with listOfProducts to Model you need */ }
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::fillProducts);
I used lambdas for shorter answer, can rewrite to Function style, but i guess it's self explanatory.
Related
I'm just exploring Rxjava in one of my android application, and got stuck at one place, honestly speaking I'm very new to this library so don't mind if my question frustrate someone;-)
So I'm trying to access the Room Database using RxJava where I'm returning the Observable List, once I get this Observable I'm trying to use map operator to get a list of ids & query again the database, which again returns me the Observable List but the map operator expects List as a return type. How can I tackle this please suggest?
Below is the code snippet:
private void getAllPcbs() {
isLoading.setValue(true);
getCompositeDisposable().add(
getRepositoryManager().loadAllPcbDetails()
.flatMap((Function<List<PcbDetails>, ObservableSource<?>>) pcbDetails -> {
List<Long> pcbList = new ArrayList<>();
for (PcbDetails details : pcbDetails)
pcbList.add(details.getPcbId());
return getRepositoryManager().loadAllPcbs(pcbList);
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onSuccess, this::onError)
);
}
private void onError(Throwable throwable) {
isLoading.setValue(false);
}
private void onSuccess(Object o) {
isLoading.setValue(false);
pcbList.setValue((List<Pcb>) o);
}
public interface DbHelper {
Observable<List<PcbDetails>> loadAllPcbDetails();
Observable<List<Pcb>> loadAllPcbs(List<Long> pcbIdList);
}
Go like
getRepositoryManager().loadAllPcbDetails()
.flatMapIterable {
listPcbDetail-> listPcbDetail
// listPcbDetail is ArrayList<PcbDetails>
// Converts your list of ids into an Observable
// which emits every item in the list
}
.flatMap { pcbDetail ->
// pcbDetail is PcbDetails
getRepositoryManager().loadAllPcbs(pcbDetail.pcbIdList)
}.subscribe { listPcb ->
// listPcb is ArrayList<Pcb>
}
Current code
Observable.from(listMovie)//list of movie
.flatMap(new Func1<Movie, Observable<FavMovieRes>>() {
#Override
public Observable<FavMovieRes> call(Movie movie) {
return moviesAPI.makeMovieFav(userId),
sessionId, new MakeMovieFav("movie", movie.getId(), movie.isFavList()));
}
})
.subscribe(new Subscriber<FavMovieRes>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(FavMovieRes favMovieRes) {
}
});
In above code I am passing Movie object list to the Observable and perform an operation on each movie instance in list when result gets from the API I want to change some database regarding that movie instance how can I get each Movie instance in OnNext() as well as onError method of subscribe method.
what I want is
Observable.from(listMovie)
.flatMap(new Func1<Movie, Observable<FavMovieRes>>() {
#Override
public Observable<FavMovieRes> call(Movie movie) {
return moviesAPI.makeMovieFav(String.valueOf(SharedPreferenceDataManager.getUserId(SyncFavListPeriodicTask.this)), SharedPreferenceDataManager.getSessionId(SyncFavListPeriodicTask.this), new MakeMovieFav("movie", movie.getId(), movie.isFavList()));
}
}).subscribe(new Subscriber<FavMovieRes>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e,Movie movie) {//or MakeMovieFav makeMovieFav
}
#Override
public void onNext(FavMovieRes favMovieRes,Movie movie) {//or MakeMovieFav makeMovieFav
}
});
I guess you have a List and want to process each received item for another single async operation. For this case you can flatmap each result.
flatMapIterable means that it will split each item in the list as Observable to the next Operation in your Stream. flatMap means that it will do an operation on the value received.
If you want to put the results back together you can use toList after flatMap.
You need to create an Observable (Operation) for your flatmap.
Kotlin:
Observable.just(data)
.flatMapIterable { it }
.flatMap{ moviesAPI.makeMovieFavObservable(whatEver) }
.subscribe( ... , ... , ... )
Java (Untested)
Observable.just(data)
//parse each item in the list and return it as observable
.flatMapIterable(d -> d)
// use each item and to another observable operation
.flatMap(data -> Observable.just(moviesAPI.movieStuff(data)))
// use each result and put it back into a list
.toList() // put the result back to a list
// subscribe it and log the result with Tag data, throw the error and output when completed
.subscribe( data -> Log.d("Data", "Data received "+ data),
error -> error.printStackTrace(),
() -> Log.d("Completed", "Completed")
);
I develop an app with mpv (mosby3) + socket.io.
I want to use rxjava 2 to relate provider and repository.
I have CategoryManager
public class CategoryManager {
private List<Category> list = null;
...
}
If list not null i can do it
public Single<List<Category>> getList() {
return Single.just(this.list);
}
But if I need load the list I have do it async like a it
socket.on("category", (data) -> {
Type founderListType = new TypeToken<ArrayList<Category>>() {}.getType();
list = gson.fromJson(data[0].toString(), founderListType);
// here i need to generate event to single subscriber
})
I think i should use to
public Single<List<Category>> getList(int count) {
return Single.create(s -> {
if (list == null) {
socket.emit("category");
// i need async load list
} else {
s.onSuccess(list.subList(0, count));
}
});
}
And CategoryPresenter should have code like
disposable.add(session.getCategoryManager()
.getList(5)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
if (isViewAttached()) {
getView().setData(data);
}
}, throwable -> {
throwable.printStackTrace();
})
);
}
I think i can keep subscribers in class property
private List<SingleEmmiter> subscribers;
and remove subscribers in setCancellable method, but i don't think it good idea.
Help me please :)
I want to implement a Rx Search view that will filter a recycler view with the string and also check the local Realm db and make a retrofit request, then combines all the results without duplicates ?? So, in other words: I would like to on text change event in a search view, to use the input string to make a network request, a db query and combine with results without duplicates
This is my initial code:
RxSearchView.queryTextChanges(searchView)
.filter(charSequence -> !TextUtils.isEmpty(charSequence))
.throttleLast(100, TimeUnit.MILLISECONDS)
.debounce(200, TimeUnit.MILLISECONDS)
.onBackpressureLatest()
.flatMap(charSequence -> userListPresenter.search(charSequence))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.onErrorResumeNext(Observable.empty())
.subscribe((Action1) response -> userListPresenter.showSearchResult((List<UserModel>) response));
The userListPresenter.search(charSequence)) should return an observable of the concatenated response without duplicates, thanks :)
userListPresenter.search(charSequence)):
Observable.create(subscriber -> {
if (Utils.isNetworkAvailable(getContext())) {
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(restApi.search(query));
subscriber.onCompleted();
}
}).mergeWith(
Observable.create(subscriber -> {
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(realmManager.getWhere(query));
subscriber.onCompleted();
}
})).collect(HashSet::new, HashSet::add)
.flatMap(Observable::from);
if I correctly understood your question. There is good way to combine the emissions of multiple Observables, look at zip method.
So, Your search method would be look like that:
public Observable<List<MyItem>> search(CharSequence query) {
return Observable.zip(
observeNetwork(query),
observeRealm(query),
new Func2<List<MyItem>, List<MyItem>, List<MyItem>>() {
#Override
public List<MyItem> call(List<MyItem> networkResult, List<MyItem> databaseResult) {
return merge(networkResult,databaseResult);
}
}
);
}
private Observable<List<MyItem>> observeRealm(CharSequence searchString) {
return Observable.create( /* do your realm stuff */);
}
private Observable<List<MyItem>> observeNetwork(CharSequence searchString) {
return return Observable.create( /* do your network stuff */);
}
private List<MyItem> merge(List<MyItem> networkResult, List<MyItem> databaseResult) {
List<MyItem> result = new ArrayList<>();
result.addAll(databaseResult);
for(MyItem newItem : networkResult){
if(!databaseResult.contains(newItem)){
result.add(newItem);
}
}
return result;
}
Using the following objects :
Order {
int orderId;
List<Item> items;
}
Item {
int price;
String description;
boolean free;
}
The goal is to keep non-free items starting from an Observable<0rder> and still return an Observable<0rder>.
I'm doing the following for now, but my items doesn't get filtered :
getMyOrder() // returns Observable<Order> from the network
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.flatMap(
order -> Observable.from(order.items)
.filter(item -> !item.isFree())
.map(items -> order)
);
This doesn't feel like something that should be done using RxJava (especially if you want to emit Order after the filtering step), could be an instance method in Order, for example:
getMyOrder().subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.map(Order::removeFreeItems)
.subscribe();
class Order {
int orderId;
List<Item> items;
public Order removeFreeItems() {
for (Iterator<Item> iterator = items.iterator(); iterator.hasNext(); ) {
if (iterator.next().free) {
iterator.remove();
}
}
return this;
}
}