I'm getting an unwanted behavior/flaw when passing a list of Observable Network calls to Observable.Zip(), similar to this accepted answer:
And the unwanted behavior is, the network calls are being fired twice...
It's fired once when the Observable is added to the List, and then it's fired again during the Observable.zip()
Here's a boiled down snippet from my project that's reproducing the behavior:
fun buildListOfObservableNetworkCalls(): Observable<Map<String, String?>> {
val clients = mutableListOf<Observable<NetworkResponse>>()
if (NetworkClient1.featureFlag) {
val postBody = someMethodToBuildPostBody()
clients.add(NetworkClient1().executeClient(postBody))
}
//There will be multiple NetworkClients in the near future
return executeAllNetworkClients(headerBidClients)
}
private fun executeAllNetworkClients(clients: List<Observable<NetworkResponse>>): Observable<Map<String, String?>> =
if (clients.isNotEmpty()) {
Observable.zip(clients) {
it
}
.map { clientResults ->
clientResults.forEach { response ->
if (response is NetworkResponse) {
map[MY_KEY] += response.stringResult
}
}
map
}.doOnSubscribe {
android.util.Log.d("LOGGER", "zip ON SUBSCRIBE")
}
} else {
Observable.just(mapOf())
}
//**** My NetworkClient1 class containing the RxJava function that executes the network call ****//
override fun executeClient(postBody: CustomPostBody): Observable<NetworkResponse> =
retrofitApiInterface.networkCall1Request(postBody)
.subscribeOn(Schedulers.io())
.doOnSuccess { response ->
Log.d("LOGGER", "Client1 ON SUCCESS")
}
.flatMapObservable { response ->
Observable.just(
NetworkResponse(
response
)
)
}.onErrorResumeNext { throwable: Throwable? ->
android.util.Log.d("LOGGER", "Client1 ON ERROR")
Observable.just(
NetworkResponse(
""
)
)
}.doOnSubscribe {
android.util.Log.d("LOGGER", "Client1 ON SUBSCRIBE")
}
//***** And my custom Interceptor which logs the double requests ****////
class MyInterceptor: Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
Log.d("LOGGER", "intercept request at ${System.currentTimeMillis()}")
val response = chain.proceed(request)
Log.d("LOGGER", "intercept response at ${System.currentTimeMillis()}")
return response
}
}
And the log output is:
D/LOGGER: zip ON SUBSCRIBE
D/LOGGER: Client1 ON SUBSCRIBE
D/LOGGER: intercept request at 1650059924358
D/LOGGER: intercept response at 1650059925747
D/LOGGER: Client1 ON SUCCESS
D/LOGGER: intercept request at 1650059925782
D/LOGGER: intercept response at 1650059925928
As you can see, the same network call is being executed twice.. and secondly, i'm also a bit puzzled as to why doOnSuccess isnt also called twice.
So, my main questions is, is there a way that I can build a list of Observables and pass it to Observable.zip() without executing the network call twice?
I see the issue is that I'm creating a List<Observable<NetworkResponse>>
and in order to add network calls that return <Observable<NetworkResponse>>
, I have to invoke the method as i'm adding them to the list. I know this may sound like a dumb question.. but is it at all possible to have a set-up where i'm able to add the Observable functions to the List without executing them? Probably over doing it, but would creating an extension function of .zip(iterable) which accepts a list of NetworkClients as the sources
param and within the extension function, execute source.executeClient()
be a feasible or stable solution?
I feel it would be inefficient if this was the unavoidable consequence of building a list of Observables to pass to zip(iterable), so i'm hoping that this is just a matter of my set-up rather than an overlooked consequence of the .zip(iterable) method.
I'm aware that I could avoid the above scenario by trying to pass each Observable Network call individually into the .zip() and use some sort of BiFunction to tie it all together. However, that doesn't seem very intuitive for my use case, being that I have to featureFlag check and build Post objects for each request that i'll be doing. Additionally, I'll be adding more NetWorkClients who's responses will all be returning the same base response type over the next few months, so I find the .zip(iterable) methodology as a clean and very scalable way of plugging in new NetworkClients.
zip
? Maybe you subscribe twice? – Pouloszip
and once for NetworkClient1's network call. – StraubsubscribeOn
temporarily to not get confused by thread switches. – Poulos