I'm trying to learn to use Rxjava to make Api calls with retrofit. I have to make multiple api calls in a loop. I'm struggling with getting the value in my subscriber.
#GET("pokemon" + "/{id}")
fun getPokemonData(#Path("id") id: Int):
Observable<Pokemon>
I'm expecting to get a Pokemon object in my Subscriber but instead I get a Observable. How do I transform it to a Pokemon object?
Observable.fromIterable(list)
.flatMap { it ->
Observable
.just(it.url)
.map { PokeApi.retrofitService.getPokemonData(getPokemonIdFromUrl(it))
}
}
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
//onNext --I'm expecting to get a Pokemon Object here, instead I get a Observable<Pokemon>
}, {//onError} , {// do something when all api calls are done?})
My goal is to make api calls with ids in the "list" and get "notified" when all the api calls are finished. Is this the correct approach to solve this problem ?
The problems lies here:
Observable
.just(it.url)
.map { PokeApi.retrofitService.getPokemonData(getPokemonIdFromUrl(it)) }
When you use map it maps to the return object from getPokemonData. You probably want to flatMap it:
Observable
.just(it.url)
.flatMap { PokeApi.retrofitService.getPokemonData(getPokemonIdFromUrl(it)) }
which not only maps the result but flattens it too so you don't get an observable, but the result of that observable.
Related
I want to get the data of a PagingData<T> object in some in-between class like ViewModel before it is reached to the Adapter and gather its property as a list.
I can access the code by the flatmap but I don't want to apply any changes. Also, this kind of access is useless because the entire block runs after subscribing to the observable is finished in ViewModel so it is unreachable by the ViewModel running.
private fun getAssignedDbList(): Flowable<PagingData<T>> {
var list = mutableListOf<Int>()
return repository.getList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap { pagingData ->
Flowable.just(pagingData.map
{ foo ->
list.add(foo.id)
foo })
}
}
Another example, let's assume that we have this scenario:
I'm going to call another API before the Paging data is reached to the view holder. The paging data needs to be filled with a new API call response. How can I wait for the second API call to update the data of paging data?
private fun getListFromAPI(): Flowable<PagingData<T>> {
var list = mutableListOf<Int>()
return repository.getList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap { pagingData ->
//call another API calls and waiting for a response to change some of the fields of
//paging data
}
}
I want to implement method to edit a note, save it to local database (cache) and then send it to the server as a POST request. I am learning RxJava and I wanted to create Observable from the note and then apply transformations on it, like to map it to an Entity model and saving. The issue that my method returns Completable and this chain returns Observable<Completable>. How to unwrap the Completable from this Observable which I used only to start RxJava stuff. Each editNote() methods returns a Completable.
override fun editNote(note: Note): Completable {
return Observable.just(note)
.map { mapper.mapToEntity(it) }
.map { noteEntity ->
factory.getCacheDataStore().editNote(noteEntity)
.andThen { factory.getRemoteDataStore().editNote(noteEntity) }
}
}
=======================================================
UPDATE
Finally, I managed to find "a solution" but I am not sure it is correct :-)
override fun editNote(note: Note): Completable {
return Observable.just(note)
.map { mapper.mapToEntity(it) }
.flatMapCompletable { noteEntity ->
factory.getCacheDataStore().editNote(noteEntity)
.andThen { factory.getRemoteDataStore().editNote(noteEntity) }
}
}
You're looking for flatMapCompletable instead of map, because map just intercepts the stream and maps the emissions to another type, while 'flatMap' (or it's siblings), from the docs:
Transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single Observable.
You can see it's marble diagram in Here
I am working on a feature where I need to filter out the network response data based on local database data.
For example, my network layers return me a list of items and my database layer returns an observable list of ids. Now I want to only return those objects from network layer whose id matches anyone from the database layer response.
Below code fetches data from the network and saves the result to a database(cache).
factory.getRemoteDataStore()
.searchForVenues(query)
.toObservable()
.distinctUntilChanged()
.flatMap { venues ->
factory.getCacheDataStore()
.saveVenues(venues)
.andThen(Observable.just(venues))
}
I also have a method that returns a list of venues that needs to be filtered
factory.getCacheDataStore().getDislikedVenues()
Now, how do I extend the previous chain to use getDislikedVenues() Observable to filter them from the response of network response?
Sorry for such noob question, I really am struggling with this.
One way of doing this is
factory.getCacheDataStore().getDislikedVenues()
.flatMap { dislikedItems ->
factory.getRemoteDataStore()
.searchForVenues(query)
.toObservable()
.distinctUntilChanged()
.flatMapIterable { it }
.filter { !dislikedItems.contains(it.id) }
.toList()
.toObservable()
}
.flatMap { venues ->
factory.getCacheDataStore()
.saveVenues(venues)
.andThen(Observable.just(venues))
}
P.S.: As I understand, factory.getRemoteDataStore().searchForVenues(query) returns Single or Maybe. In that case distinctUntilChanged() don't work because it relies on multiple emission (onNext()), but Single or Maybe can emit only one result.
I want to call multiple Rest Api's in a Sequence and having each Response Dto is different from each other.
Please help me to get rid from this situation that, How can i call these Api's using Rx Java Observables in Android.
no, you should use map() or doOnNext(), it will look like this
Observable.just(1)
.doOnNext(value -> {
someRequestX().execute();
})
.map(value -> {
return nextRequestY().execute();
})
.doOnNext(requestYResponse-> {
someRequesZ(requestYResponse.someValue).execute();
})
.map(requestYResponse-> {
return someRequesK(requestYResponse.someValue).execute();
})
.map(requestKResponse -> {
return someRequesJ(requestKResponse.someValue).execute();
})
.subscribe(requestJResponse -> {
doSOmethingWithFinalResponse(requestJResponse );
})
First of all, for network requests is better to use Single then Observable, because there always will be only one item. To switch from one requests to another, you can use flatMap.
Assuming your code is similar, you can try this:
class Dto1 {}
class Dto2 {}
class Dto3 {}
public interface Api {
Single<Dto1> getDto1();
Single<Dto2> getDto2();
Single<Dto3> getDto3();
}
private Api api;
public void callApi() {
api.getDto1()
.doOnSuccess(dto1 -> {/*do something with dto1*/})
.flatMap(dto1 -> api.getDto2())
.doOnSuccess(dto2 -> {/*do something with dto2*/})
.flatMap(dto2 -> api.getDto3())
.doOnSuccess(dto3 -> {/*do something with dto3*/})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe()
}
For the same scenario, i use concat operator which takes multiple Observables and concatenates their sequence
If response sequence doesn't require then you can use merge operator also.
Concat VS Merge operaror
try doOnNext() or map() method of Observable and use sync execute() of each response and pass them further
To make data accessible for offline viewing I have a data layer that first requests the data from database and secondly does a network call to get data from api (and stores it to database).
F.e. say i want to get recycle scores by user id:
Datalayer:
class RecycleScoreRepository{
fun getRecycleScoresByUserId(userId: Int): Observable<RecycleScores> {
return Observable.concatArray(
getRecycleScoresFromDb(userId),
getRecycleScoresFromApi(userId))}
}
object RepositoryManager {
...
fun getRecycleScoresByUserId(userId: Int): Observable<RecycleScores> {
return recycleScoreRepository.getRecycleScoresByUserId(userId)
//Drop DB data if we can fetch item fast enough from the API to avoid UI flickers
.debounce(400, TimeUnit.MILLISECONDS)} ...
Presenter:
RepositoryManager.getRecycleScoresByUserId(userId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
// do something on success
}, {
// do something on error
})
So my presenter is subscribing to the Repository to getRecycleScoresByUserId. I am using the debounce operator to make sure that in case the api call is fast enough that i am not setting returned values twice on ui as to prevent ui flickering. But now what happens is that when the database successfully returns me some recycleScores but for some reason api request response with an error that the subscriber in the presenter only receives an error and not the observable with values from the database.
How can I make sure the database's observable is received by subscribers and not being debounced when api call returns an error?
This may be not the best solution, but you could filter error from your api observable response in this part
fun getRecycleScoresByUserId(userId: Int): Observable<RecycleScores> {
return Observable.concatArray(
getRecycleScoresFromDb(userId),
getRecycleScoresFromApi(userId)
.materialize()
.filter{ !it.isOnError }
.dematerialize<RecycleScores>()
)}
}
then your subscriber will keep getting the result. For your second question to not debounce when getting error, I have no idea how to achieve that.
Edit:
To handle error from your API response, one idea is to wrap api response into another type then you can handle it properly. For example:
sealed class RecycleResponse {
class OK(val score: RecycleScore) : RecycleResponse()
class NotOK(val error: Exception) : RecycleResponse()
}
then you can use it like these:
fun getRecycleScoresByUserId(userId: Int): Observable<RecycleResponse> {
return Observable.concatArray(
getRecycleScoresFromDb(userId),
getRecycleScoresFromApi(userId))
.map<RecycleResponse> { RecycleResponse.OK(it) }
.onErrorReturn { RecycleResponse.NotOK(it) }
}