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.
Related
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() }
I've got the following problem:
I have a Single that returns a list of objects, inside each object, there is a field that is also a Single, and that returns a list of items (which is what i need), but i also need this list to be returned as one result, rather than as each individual single..
I have an Observable that i can use which is wrapping all of this code, so i would like to emit the result in onNext.
Additionally, I also need to add the individual results of each Single into a cache, with the key being the parent object
Code:
object.getListOfObjects().doOnSuccess { objectList ->
objectList.map { singleItemInList ->
singleItemInList.listOfValues.subscribe({ valueList ->
cache[singleItemInList] = valueList
emitter.onNext(valueList)
// ^ this is the bit where i need to get all values instead of
// emitting on next for each item
},{
emitter.onError(it)
})
}
}.subscribe( {
/* no-op*/
}, {
emitter.onError(it)
})
Any help would be appreciated!!
Thanks in advance :)
EDIT - Extra details:
fun getListOfObjects(): Single<List<Object>> =
Single.zip(
allObjects().map { it.objects() }
) { t: Array<Any> ->
t.map { it as Object }
}
Object {
[...]
val listOfValues: Single<ValueList>
[...]
}
Use flatMap operator in that case and at the final step toList
object.getListOfObjects()
.flatMap { objectList -> objectList.listOfValues() }
.toList()
.subscribe({ result -> TODO() }, { error -> TODO() })
I've found an approach, but it is far from pretty... Any suggestions as to how this could be implemented more prettily would be greatly appreciated! :D
object.getListOfObjects().doOnSuccess { objectList ->
val list = objectList.map { singleItemInList ->
singleItemInList.listOfValues.subscribe({ valueList ->
cache[singleItemInList] = valueList
},{
emitter.onError(it)
})
}
}.flatten().map {
it.toObservable()
}
Observable.zip(list) { itemList ->
val compoundList = mutableListOf<Value>()
for (i in itemList.indices) {
val singleList = itemList[i] as? List<Value>
singleList?.map { item ->
compoundList.add(item)
}
}
emitter.onNext(compoundList)
// ^ this now emits the entire list instead of
// each sublist individually
emitter.onComplete()
}.subscribe({
/* no-op */
}, {
emitter.onError(it)
})
}.subscribe({
/* no-op */
}, {
emitter.onError(it)
})
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()
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.
I have a simple case i'm trying to do with rxJava 2 but i'm stuck.
I have a locationProvider which returns a Maybe (some model with LatLng). Each time a user clicks on the button, I want to get his current location and save the location to the db.
Just to be clear,
The RxView.clicks(save_btn) should be on the MainThread, the location and db save should be on an async thread, and response should come back to mainThread.
How should I do this?
addDisposeable(RxView.clicks(save_btn)
.flatMap { locationProvider.getLastKnowLocation().toObservable() }
.map {
val place = Place(UUID.randomUUID().toString(), "test-address", it.latLng)
db.placeDao().insertAll(place)
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
Toast.makeText(this#AddPlaceActivity, "saved", Toast.LENGTH_SHORT).show()
}, { throwable -> Timber.e(throwable) }))
I've also tried to simplify it to this:
RxView.clicks(save_btn).share()
.flatMap { locationProvider.getLastKnowLocation().toObservable() }
.subscribe({
val place = Place(UUID.randomUUID().toString(), "test-address", it.latLng)
db.placeDao().insertAll(place)
Timber.d("place-saved")
}, { throwable -> Timber.e(throwable) }))
But this code only execute for the first click and doesn't continue after.
Is it due to the Maybe.toObservable()? how should it be resolved?
Thanks for your help.
This is the locationProvider's code, i'm using RxLocation:
fun getLastKnowLocation(): Maybe<LocationData> {
return rxLocation.location()
.lastLocation()
.map {
val address = geocoder.getFromLocation(it.latitude, it.longitude, 1).first()
val latlng = LatLng(it.latitude, it.longitude)
LocationData(latlng, address)
}
}
Unfortunately,the user who set me in the correct path deleted his answer :(
This is what I ended up with
addDisposeable(
RxView.clicks(save_btn).share()
.observeOn(Schedulers.computation())
.withLatestFrom(locationProvider.getLastKnowLocation().toObservable(), BiFunction { _:Any, l:LocationData -> l })
.map {
val place = Place(UUID.randomUUID().toString(), "test-address", it.latLng)
db.placeDao().insertAll(place)
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
Timber.d("place-saved")
}, { throwable -> Timber.e(throwable) }))
I couldn't find a nicer way for the 'BiFunction' part, if anyone has something nicer instead.
Also note that you need to call observeOn twice for the thread switching part.