Chaining requests in Retrofit + RxJava
I don't think using map
operator is the best way to go with things like storing the result of the api call.
What I like to do is to separate those things inside doOnNext
operators. So your example would be something like this:
apiService.A()
.doOnNext(modelA -> db.store(modelA))
.flatMap(modelA -> apiService.B())
.doOnNext(modelB -> db.store(modelB));
(add necessary observeOn
and subscribeOn
yourself, exactly like you need them)
Chaining API Requests with Retrofit + Rx
Single as a return type in your case will not be the best option because it is designed to only maintain single stream. concatMap or flatMap on Single will not either because it will try to map list of items to another list of items which is not the case
here.
Instead you could use Observable or map your Single to Observable by using toObservable() with concatMapIterable operator which maps your single item to sequence of items.
I used concatMap operator instead of flatMap because it maintains order of the list items so your data won't be mixed up.
getTopStoryIDs()
.map { it.take(20) }
.toObservable()
.concatMapIterable { it }
.concatMapSingle { singleId ->
api.getItem(singleId)
}
.toList()
.subscribe { items ->
//do something with your items
}
This code will work but it's not the best solution because you will make 20 or more api calls which will hurt your network data and device battery so I wouldn't use it if it is not completely necessary.
If you have any questions fill free to ask :)
RXJava2: correct pattern to chain retrofit requests
I would suggest using flat map (And retrolambda if that is an option).
Also you do not need to keep the return value (e.g Single<FirstResponse> first
) if you are not doing anything with it.
retrofitService.getSomething()
.flatMap(firstResponse -> retrofitService.getSecondResponse(firstResponse.id)
.subscribeWith(new DisposableSingleObserver<SecondResponse>() {
@Override
public void onSuccess(final SecondResponse secondResponse) {
// we're done with both!
}
@Override
public void onError(final Throwable error) {
// a request request Failed,
}
});
This article helped me think through styles in how I structure RxJava in general. You want your chain to be a list of high level actions if possible so it can be read as a sequence of actions/transformations.
EDIT
Without lambdas you can just use a Func1
for your flatMap. Does the same thing just a lot more boiler-plate code.
retrofitService.getSomething()
.flatMap(new Func1<FirstResponse, Observable<SecondResponse> {
public void Observable<SecondResponse> call(FirstResponse firstResponse) {
return retrofitService.getSecondResponse(firstResponse.id)
}
})
.subscribeWith(new DisposableSingleObserver<SecondResponse>() {
@Override
public void onSuccess(final SecondResponse secondResponse) {
// we're done with both!
}
@Override
public void onError(final Throwable error) {
// a request request Failed,
}
});
chaining requests with retrofit and rxjava
You can use flatmap function for this exact purpose
textsCall
.flatMap(new Func1 < ResponseBody, Observable < AirportCombo >> () {
@Override
public Observable < AirportCombo > call(ResponseBody valueA) {
// code to save data from service valueA to db
// call service B
return routesCall;
}
})
.flatMap(new Func1 < AirportCombo, Observable < ValueC >> () {
@Override
public Observable < ValueC > call(AirportCombo valueB) {
// code to save data from service valueB to db
// call service C
return observableC;
}
})
.flatMap(new Func1 < ValueC, Observable < ValueD >> () {
@Override
public Observable < ValueD > call(ValueC valueC) {
// code to save data from service valueC to db
// call service D
return observableD;
}
})
.flatMap(new Func1 < ValueD, Observable < ValueFinal >> () {
@Override
public Observable < ValueFinal > call(ValueD valueC) {
// code to save data from service valueD to db
// call Final Service
return observableFinal;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber < ValueFinal > () {
@Override
public void onCompleted() {}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(ValueFinal fooB) {
// code to save data from service ValueFinal to db
}
});
Chaining multiple calls RxJava, Room and Retrofit
First of all try not rely on Side effects, that make things unpredictable .. for example this function
fun insertKafaniInDb(kafani: List<Kafana>) {
Observable.fromCallable { kafanaDao.insertAll(kafani) }
.subscribeOn(Schedulers.io())
.subscribe {
Timber.d("Inserted ${kafani.size} kafani from API in DB...")
}
}
its return type is Unit, it's better to include it in the stream, this is done by converting to Completable
so it will be something like this
fun insertKafaniInDb(kafani: List<Kafana>) {
return Observable.fromAction { kafanaDao.insertAll(kafani) }
.subscribeOn(Schedulers.io())
.doOnComplete { Timber.d("Inserted ${kafani.size} kafani from API in DB...") }
}
other functions that return Unit (void in java) should be converted the same way to completabel. so now I'll try to rewrite your logic without using side effects. and explaining each step.
fun getUpdatedData(): Single<MutableList<String>>? {
return kafanaRepository.getFavoriteKafani()
.toObservable()
.flatMap { Observable.fromIterable(it) } //to iterate on favorite items
.flatMap { localItem ->
kafanaRepository.getKafaniFromApi()
.flatMap { Observable.fromIterable(it) } //to iterate api items
.filter { localItem == it } //search for the favorite item in Api response
.flatMap {
//we update this item then we pass it after update
kafanaRepository.updateFavoriteKafana(it)
.andThen(Observable.just(it))
}
.defaultIfEmpty(localItem) //if it's not found, then no update needed we take this it.
}.toList() // we collect the updated and non updated local items to list
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
Hope this could help you.
Final words .. Rx is about ordering your thoughts and plug your logic in functional way .. try to avoid using onNext()
to update global variables, use it just for logging and non Business logic.
Chaining Retrofit calls with RxJava and returning the main object
Observable<List<Library>> observable = myRestInterface
.getLibraryList()
.flatMapIterable(response -> response.getItems())
.doOnNext(library -> myRestInterface
.getAuthorList(library.getUrl().substring(library.getUrl().lastIndexOf("/") + 1))
.flatMapIterable(response -> response.getItems())
.doOnNext(author -> library.getAuthors().add(author))
.doOnNext(
author -> myRestInterface
.getBooksList(author.getUrl().substring(author.getUrl().lastIndexOf("/") + 1))
.flatMapIterable(response -> response.getItems())
.doOnNext(book -> author.getBooks().add(book))
.subscribe()
)
.subscribe()
)
.doOnNext(mainObjectList -> new ViewupdateMethod(mainObjectList))
.subscribe();
Note .subscribe()
calls. They are used to start execution of the whole chain. RxObservable does nothing until subscriber arrives.
Related Topics
Filling a Circle Gradually from Bottom to Top Android
Newer Versions of Android Studio and Only Two Drawable Directory - Drawable and Drawable-V21
Setting Audio File as Ringtone
Debugging Android Ndk Native Apps
Android Button Background Color
Android Custom Layout - Ondraw() Never Gets Called
Changing the Screen Brightness System Setting Android
Cannot Deserialize Instance of Object Out of Start_Array Token in Spring Webservice
How to Rotate Textview 90 Degrees and Display
Webview Rendering Issue in Android Kitkat
Capturing Images with Mediastore.Action_Image_Capture Intent in Android
How to Retrieve HTML Content from Webview (As a String)
How to Download a File from a Server and Save It in Specific Folder in Sd Card in Android
Android:Google Maps API Key Signup:Md5 Certification Key