Reputation: 4230
I want to call an API api1
and pass the output returned by the API to a second API api2
. The first API is a get request and second API is a POST
request. Therefore, the api1
returns a Single<String>
and api2
returns a Completable
.
The functions look something like this:
// api1
public Single<String> getToken() {
...
}
// api2
public Completable saveTokenToBackend(String token, String userId) {
...
}
I want to chain both of these operations together. The subscriber is only interested in knowing if the process of getting token and saving it was successful or not. Therefore, the return type of the final chain of operations should be a Completable
. However, when i do this the API calls for api2
stop happening. Only api1
works successfully as per logs.
Single<Completable> r1 = getToken().map(t -> saveTokenToBackend(t, userId));
Completable r2 = Completable.fromSingle(r1);
My broader question here is that how do i chain a response from a Single to a Completable?
Second question is why the above code is not working?
::EDIT::
Based on suggestions in comments, I tried:
public Completable getAndSaveToken() {
getToken().flatMapCompletable(t -> saveTokenToBackend(t, "dummyuser");
}
In my application code i am doing:
getAndSaveToken()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(() -> {
Log.v(TAG, "call success");
}, error -> {
Log.e(TAG, "Error", error);
});
Results in: java.io.IOException: Must not be called on the main application thread
Upvotes: 3
Views: 5337
Reputation: 1578
You have 2 different problems, composition and scheduling. Context: getToken
return a single and saveToken(token)
returns a completable.
composition: to compose a single and a completable, as you have already noticed, you can use the flatMap operator, this returns a new completable that first gets the token, and then save it (probably, you'll want to modify the token before saving it ;)
getToken().flatMapCompletable(n -> saveToken(n)) // returns a completable
If you want to keep it as a single, so you can map it back to the first instance:
getToken().flatMap(n -> saveToken(n).toSingleDefault(n)) // returns a single
scheduling: in android, you must not start a request in the main thread. To avoid it you can use the subscribeOn
operator, also looks like you have already noticed that
getToken().subscribeOn(io()).doOnNext(n -> {/*this should be evaluated in io thread*/})
getToken().subscribeOn(io()).observeOn(mainThread()).doOnNext(n -> {/*this on mainThread*/})
If you still get the error, then the observeOn
or subscribeOn
has been re-configured somewhere. Need more code to be sure. But anyways, you can assert that both requests are executed in the io thread applying the operator twice:
getToken().subscribeOn(io).flatMapCompletable(token -> saveToken(token).subscribeOn(io()))
Another alternative, if you are using retrofit, is to apply the RxJava2CallAdapterFactory
using the createWithScheduler(io())
factory, instead of the default one. This will assert that all requests are created in the io(), so you can combine and prepare the data, and finally apply the observeOn(mainThread())
to update the UI.
Upvotes: 7