Avoid Inserting item in a list on Error code - android

So I have a function which is placing a call using RxJava and then this function is supposed to insert the element returned into a list.
So the code works fine. I am able to retrieve and all items in the cart list. However if an error code generated by the getItem appears (getItem place an API call) such 401 or 404..., I want to continue in the iteration and just bypass the insert. I can't use onErrorReturnItem with a null and filter on null after.
return Observable.just(itemResponses)
.flatMapIterable { it }
.flatMapSingle { itemResponse ->
itemWarehouse.getItem(itemResponse.id)
.map { item ->
itemData(
itemResponse.id,
item,
itemResponse.info,
false,
itemResponse.result)
}
}
.map { itemData -> cartMap[itemData.id]?.insert(itemData) }
.toList()
.map { cartMap.values.toList() }
}
Any idea ?

This should work
return Observable.just(itemResponses)
.flatMapIterable { it }
.flatMap { itemResponse ->
itemWarehouse.getItem(itemResponse.id)
.toObservable()
.map { item ->
itemData(
itemResponse.id,
item,
itemResponse.info,
false,
itemResponse.result)
}
.onErrorResumeNext(Observable.empty())
}
.map { itemData -> cartMap[itemData.id]?.insert(itemData) }
.toList()
.map { cartMap.values.toList() }

Related

RxJava: How to transform a Single<Response> to Single<List<Item>>

Let's say I have an Android app with the following Repository class to fetch objects from API:
override fun fetchOrderById(orderId: Long): Single<List<ItemRow>> {
return api.fetchOrderByIdObservable(orderId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map { orderResponse ->
orderResponse.items.map { deliveryItem ->
deliveryItem.asItemRow()
}
}
}
override fun fetchOrders(): Single<OrdersResponse> {
return api.fetchOrdersObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
override fun fetchAllOrders(): Single<List<ItemRow>> {
// TODO
}
With the following data class:
class OrdersResponse(#SerializedName("orders") val orders: List<Long>)
class OrderResponse(#SerializedName("items") val items: List<DeliveryItem>)
Right now, I can use fetchOrderById to get all delivery items (ItemRow) for a certain orderId as a domain object in my app to show in a list. How can I use fetchOrders, which returns a list of orderIds to get all delivery items for all orders? Which operators would be useful here? I played around with FlatMap, but could not get it to work. Link to articles would be ++ too. Thanks!
operators you need: switchMap(), flatMap(), fromArray(), toList(), map() it's gonna be something like this:
fun fetchAllOrders(): Observable<List<ItemRow>> {
return fetchOrders()
.toObservable()
.switchMap { ordersResponse ->
Observable.fromIterable(ordersResponse.orders)
.flatMap {orderId -> fetchOrderById(orderId).toObservable() }
.toList()
.toObservable()
}
.map { list -> list.flatten() }
}
This worked eventually:
public fun fetchAllItems(): Observable<List<ItemRow>> {
return networkService.api.fetchOrdersObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap { orderResponse -> Observable.just(orderResponse.orders) }
.flatMapIterable { id -> id }
.flatMap { id -> fetchOrderById(id) }
.flatMapIterable { itemRow -> itemRow }
.toList()
}

Pass value from single across completable to result in rxjava Android

This is what I want:
Check if I have data about products in database.
If I have data I run Single to get data from DB.
If not I run Single for get data from backend
If I get response I want to save data in DB using Completable.
After saving data I want to map values from step 2 or 3 to view model
In result I want to send data to activity.
This is what I have now:
checkProductsInDBUseCase.run()
.flatMap {
if (it) {
getProductsFromDBUseCase.run()
} else {
getProductsUseCase.run(3)
}
}.map {
it.products.map { item -> item.toViewModel() }
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy(
onSuccess = {
view.showBikes(it)
},
onError = {
view.showBikesError(it.message.toString())
}
).addTo(disposables)
Between flat map and map I need to run saveDataUseCase(it), but I don't know how to pass itfrom completable to map. Any ideas?
If your saveDataUseCase() is Completable then you can do this
checkProductsInDBUseCase.run()
.flatMap {
if (it) {
getProductsFromDBUseCase.run()
} else {
getProductsUseCase.run(3)
}
}
.faltMap {
saveDataUseCase(it).toSingleDefault(it)
}
.map {
it.products.map { item -> item.toViewModel() }
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy(
onSuccess = {
view.showBikes(it)
},
onError = {
view.showBikesError(it.message.toString())
}
).addTo(disposables)
But if you change return type of saveDataUseCase() to Unit, you can use Fred's answer. It would be better
Here I'd use doOnSuccess. This seems ideal especially because you're creating a side effect, which we usually use the doOnXXX methods for.
checkProductsInDBUseCase.run()
.flatMap {
if (it) {
getProductsFromDBUseCase.run()
} else {
getProductsUseCase.run(3)
}
}
.doOnSuccess {
saveDataUseCase(it)
}
.map {
it.products.map { item -> item.toViewModel() }
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy(
onSuccess = {
view.showBikes(it)
},
onError = {
view.showBikesError(it.message.toString())
}
).addTo(disposables)
The method will not change the result of the flatMap so you will still get the correct object inside the map function.

Android multithreading data

How take process multithreaded load for the item list. I get from api list string elements. Next need to get data for items this list. Load need to use rxjava. Result need do getting to the single subscribe.
Next need to get data for items this list
What it has to be? Just method inside your class or separate network request for each of strings?
For first case:
getListFromApi()
.toFlowable()
.flatMap { Flowable.fromIterable(it) }
.map { getSomeData(it) }
.toList()
.subscribe()
Second case:
getListFromApi()
.toFlowable()
.flatMap { Flowable.fromIterable(it) }
.map { requestForSomeData(it) }
.toList()
.flatMap { flowablesList -> Single.zip(flowablesList.map { it.firstOrError() }) { it.toList() } }
.subscribe()

RxJava2. Execute a request for every item in a list

I have a list List<FileModel>
FileModel is just a class contains id: Int
id - is ID of photo file I need to fetch and cast to Bitmap
I have a request:
fun getFile(fileId: Int): Single<ResponseBody>
This request returns ResponseBody which we can cast to Bitmap
And
fun generatePhoto(responseBody: ResponseBody): Single<Bitmap?>
What I want is to create a function
fun getPhotos(list: List<FileModel>): Single<List<Bitmap>> {
// Execute getFile(...) for each item in the list
// Cast getFile(...) result to Bitmap using generatePhoto(...)
// Return a list of Bitmaps
}
I've tried something like this but it's completely wrong
fun getPhotos(list: List<FileModel>): Single<List<Bitmap>> {
return Observable.fromIterable(list)
.flatMap { getFile(it.id) }
// How to call generatePhoto(...) here?
}
You can do it like this:
fun getPhotos(list: List<FileModel>): Single<List<Bitmap>> {
// Get an Observable of the list
return Observable.fromIterable(list)
// Get a Single<ResponseBody> for every FileModel
.flatMapSingle { getFile(it.id) }
// Get a Single<Bitmap> for every ResponseBody
.flatMapSingle { file -> generatePhoto(file) }
// Put everything back on a list
.toList()
}
This way you can iterate over the list flapMapping for your needs and then putting it back together as a list in the end. The toList() operator is just a convenience that puts together the items emitted previously.
And to call this function just go:
getPhotos(list)
.doOnSuccess { resultList ->
Log.d("Rx", "doOnSuccess.resultList=[$resultList]")
}
.subscribe()
By the way, if you have RxKotlin as a dependency you can get an Observable from a List with an extension function, like this:
myList.toObservable()
Observable.fromIterable(list) should emit an event for each item in the list which means that you're return type in getPhotos be Flowable<Bitmap> as Flowable can return more than one value.
I think what you actually want is:
fun getPhotos(list: List<FileModel>): Flowable<Bitmap> {
return Observable.fromIterable(list)
.map { getFile(it.id) }
.map { generatePhoto(it) }
}
if you want to emit a list of Bitmaps you can use single like this
fun getPhotos(list: List<FileModel>): Single<List<Bitmap>> {
return Single.just(list)
.map { it.map { getFile(it.id) } }
.flatMap {
Single.just(it.map { generatePhoto(it) })
}
}
Have you tried:
fun getPhotos(list: List<FileModel>): Single<List<Bitmap>> {
return Observable.fromIterable(list)
.flatMapSingle { getFile(it.id) }
.flatMapSingle { generatePhoto(it) }
}
?

Filter Observable<List> with text from an EditText using RxBinding

I have an array of Objects and I want to filter that array based on the text user types on an EditText android view.
What I thought it that I should try and convert the array of POJOs to an Observable of Strings and this is what I did :
Observable<String> professionsObservable = Observable.fromArray(((GetStartedActivity) getActivity()).professions)
.map(profession -> {
if (profession.getName().length() > 0) {
professionsNameList.add(capitalizeFirstLetter(profession.getName()));
}
return professionsNameList;
})
.flatMapIterable(items -> items);
Now I want to combine the text from the EditText with the `professionsObservable I posted above.
This is the code I'm using :
RxTextView.textChangeEvents(etProfession)
.doOnEach(notif -> {
if (etProfession.getText().toString().trim().length() > 0) {
etCompany.setVisibility(GONE);
etIndustry.setVisibility(GONE);
} else {
etCompany.setVisibility(VISIBLE);
etIndustry.setVisibility(VISIBLE);
}
})
.debounce(EDITTEXT_DELAY, TimeUnit.MILLISECONDS)
.skip(1)
.map(textChangeEvent -> textChangeEvent.text().toString())
.switchMap(search -> {
return professionsObservable
.filter(profession -> {
return profession.toLowerCase().startsWith(search);
});
}
)
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
filteredProfessions -> {
Timber.i("NOT ENTERING");
rvProfession.setVisibility(VISIBLE);
professionAdapter.addItems(filteredProfessions);
},
throwable -> Log.i("THROW", "PROFESSIONS ", throwable));
I'm using map operator to turn the text change event to a String and then for each String I get from the stream I'm using switchMap (cause I don't care for results from previous searches). Then I compose all Strings to a List with toList. The problem is that it never reaches the subscribe call while I have a lot of strings in the initial Array I used and I do type text that fits the condition of the filter operator.
Is it something that I might missing here ?
EDIT : I updated my code to :
RxTextView.textChangeEvents(etProfession)
.doOnEach(notif -> {
if (etProfession.getText().toString().trim().length() > 0) {
etCompany.setVisibility(GONE);
etIndustry.setVisibility(GONE);
} else {
etCompany.setVisibility(VISIBLE);
etIndustry.setVisibility(VISIBLE);
}
})
.subscribeOn(AndroidSchedulers.mainThread())
.debounce(EDITTEXT_DELAY, TimeUnit.MILLISECONDS)
.skip(1)
.map(textChangeEvent -> textChangeEvent.text().toString())
.flatMap(search -> {
return Observable.fromArray(((GetStartedActivity) getActivity()).professions)
.map(profession -> {
List<String> professionsList = new ArrayList<>();
if (profession.getName().length() > 0) {
professionsList.add(capitalizeFirstLetter(profession.getName()));
}
return professionsList;
})
.flatMapIterable(items -> items)
.filter(profession -> {
if (profession.toLowerCase().startsWith(search.toLowerCase())) {
}
return profession.toLowerCase().startsWith(search.toLowerCase());
});
})
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
filteredProfessions -> {
rvProfession.setVisibility(VISIBLE);
// professionAdapter.addItems(filteredProfessions);
},
throwable -> Log.i("THROW", "PROFESSIONS ", throwable));
If I remove toList() operator my code works (enters the subscribe call) but if I leave it there it wont. Anyone knows why ?
In my experience, RxBinding requires .subscribeOn(AndroidSchedulers.mainThread()) right after .textChangeEvents() or any other event. So probably you are causing it to fail by adding .subscribeOn(Schedulers.io)
See method .checkMainThread() in https://github.com/JakeWharton/RxBinding/blob/master/rxbinding/src/main/java/com/jakewharton/rxbinding2/internal/Preconditions.java
UPDATE
Also, probably because .onComplete() never comes from upstream, .toList() is never executed.

Categories

Resources