Why doesn't doOnNext() get called? - android

Neither onNext() nor onCompleted() get called for my subscriber below. I've tried implementing the subscriber via doOnNext()/doOnTerminate(). I also tried doAfterTerminate(). I've also tried explicitly defining a subscriber and neither onNext() nor onCompleted() got called for it.
According to RxJS reduce doesn't continue, it's the reduce() that's not terminating so I tried adding the take(1) but that didn't work. In the same stackoverflow question someone said the problem might be my stream never closes. Aside from take(1), maybe there is some other way I should close the stream, but I don't understand ReactiveX well enough yet.
According to Why is OnComplete not called in this code? (RxAndroid), it could be that the original stream in the series doesn't terminate. But I don't see why that would matter if I'm calling take(1) which I think is supposed to emit a termination signal.
Basically, why doesn't the following line get executed?
System.out.println("doOnNext map.size()=" + map.size());
Even though the following line of code gets executed 98 times:
map.put(e.getKey(), e.getValue());
JsonObjectObservableRequest.java
import com.android.volley.toolbox.JsonObjectRequest;
...
public class JsonObjectObservableRequest {
public JsonObjectObservableRequest(int method, String url, JSONObject request) {
jsonObjectRequest = new JsonObjectRequest(method, url, request, getResponseListener(), getResponseErrorListener());
}
private Response.Listener<JSONObject> getResponseListener() {
return new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
publishSubject.onNext(Observable.just(response));
}
};
}
private Response.ErrorListener getResponseErrorListener() {
return new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Observable<JSONObject> myError = Observable.error(error);
publishSubject.onNext(myError);
}
};
}
public JsonObjectRequest getJsonObjectRequest() {
return jsonObjectRequest;
}
public Observable<JSONObject> getObservable() {
return publishSubject.flatMap(new Func1<Observable<JSONObject>, Observable< JSONObject>>() {
#Override
public Observable<JSONObject> call(Observable<JSONObject> jsonObjectObservable) {
return jsonObjectObservable;
}
});
}
}
JsonObjectObservableRequest calling code
import com.android.volley.Request;
import com.android.volley.RequestQueue;
...
JsonObjectObservableRequest jsonObjectObservableRequest = new JsonObjectObservableRequest(Request.Method.GET, idURLString, null, keyId, key);
Observable<JSONObject> jsonObjectObservable = jsonObjectObservableRequest.getObservable();
jsonObjectObservable
.map(json -> {
try {
return NetworkAccountIdDatasource.parseIdJSON(json);
} catch (JSONException e) {
e.printStackTrace();
return null;
}
})
.flatMapIterable(x -> x)
.map(s -> new AbstractMap.SimpleEntry<String, String>("Name of " + s, "short id for " + s.substring(4)))
.reduce(new HashMap<String, String>(), (map, e) -> {
map.put(e.getKey(), e.getValue());
return map;
})
.take(1)
.doOnNext(map -> {
System.out.println("doOnNext map.size()=" + map.size());
})
.doOnTerminate(() -> {
System.out.println("doOnTerminate");
})
.subscribe();
final RequestQueue queue = Volley.newRequestQueue(context);
queue.add(jsonObjectObservableRequest.getJsonObjectRequest());

You identified correctly the problem, you're not calling onCompleted() on the source network Observable (at the JsonObjectObservableRequest class), while the take(1) is not helpful either because you put it before the reduce.
As you were probably understood, reduce must operate on Observable with finite number of emissions, as it's emit the accumulated item when all items have been emitted and thus it's relies on the onCompleted() event to know when the observed stream has end.
I think that in your case, the best thing is to change the way JsonObjectObservableRequest operates, you don't need a Subject for this, you can use creation methods to wrap a callback (you can see my answer here, and read more about bridging between callback world to RxJava here.
Moreover, you don't need the emit an Observable of something and then flatmap it to items, you can simply emit the item with onNext() and emit an error with onError(), actually you're hiding the errors and converting them to an emission, this might make it harder to handle errors down the stream.
You should callonCompleted() at onResponse() after you call onNext() to signal the completion. and in the case of error, signaling onError will notify termination of the stream.
Another concern is canceling the request which seems not handled in this way, you can read the sources up to see how it can be done when wrapping callbacl call.

I found a way to get the doOnTerminate() and doOnNext() to be called. It was to place the take(1) statement higher up in the stream processing. I got this idea based on the comment from #yosriz . I wish I understood why this fixed the problem, but I don't because I thought a termination signal would just get propagated through, but I guess there is a lot more I need to learn about ReactiveX / reactive functional programming / etc..
JsonObjectObservableRequest calling code
import com.android.volley.Request;
import com.android.volley.RequestQueue;
...
JsonObjectObservableRequest jsonObjectObservableRequest = new JsonObjectObservableRequest(Request.Method.GET, idURLString, null, keyId, key);
Observable<JSONObject> jsonObjectObservable = jsonObjectObservableRequest.getObservable();
jsonObjectObservable
.map(json -> {
try {
return NetworkAccountIdDatasource.parseIdJSON(json);
} catch (JSONException e) {
e.printStackTrace();
return null;
}
})
.take(1) // SOLUTION: MOVED THIS UP FROM BELOW reduce()
.flatMapIterable(x -> x)
.map(s -> new AbstractMap.SimpleEntry<String, String>("Name of " + s, "short id for " + s.substring(4)))
.reduce(new HashMap<String, String>(), (map, e) -> {
map.put(e.getKey(), e.getValue());
return map;
})
// .take(1) // SOLUTION: MOVE THIS ABOVE flatMapIterable()
.doOnNext(map -> {
System.out.println("doOnNext map.size()=" + map.size());
})
.doOnTerminate(() -> {
System.out.println("doOnTerminate");
})
.subscribe();
final RequestQueue queue = Volley.newRequestQueue(context);
queue.add(jsonObjectObservableRequest.getJsonObjectRequest());
Furthermore, it works also if I put take(1) as the very first operation, before the first map() operation (the one that calls parseIdJSON()). However, if I put take(1) as the operation just before reduce(), it only partially works; the good thing is doOnNext() gets called but the bad thing is the map.size() is always 1 when it should be 98 for my data set.

Related

Should I use Observable(RxJava2) or Call (retrofit2)?

TL;DR: I want to execute multiple Calls (Retrofit) like you can .zip() multiple Observables (RxJava2).
I have a retrofit2 function:
#GET("/data/price")
Call<JsonObject> getBookTitle(#Query("id") String id, #Query("lang") String lang);
I can execute it (async) in code with enquene():
ApiProvider.getBooksAPI().getBookTitle(bookId, "en").enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) { }
#Override
public void onFailure(Call<JsonObject> call, Throwable t) { }
});
Now I want to execute multiple Calls at once (get multiple book titles) and be notified when all requests are done. Here is when I am missing knowledge.
I know I could start using Observable (RXJava2) instead of Call (Retrofit2):
#GET("/data/price")
Observable<JsonObject> getBookTitle(#Query("id") String id, #Query("lang") String lang);
and then merge calls like in below example. But this code seems much more complex and long (especially if I only need 1 book title). Isn't there any way I could merge Calls without using Observable?
List<Observable<JsonObject>> mergedCalls = new ArrayList<>();
mergedCalls.add(ApiProvider.getBooksAPI().getBookTitle(bookId1, "en"));
mergedCalls.add(ApiProvider.getBooksAPI().getBookTitle(bookId2, "en"));
mergedCalls.add(ApiProvider.getBooksAPI().getBookTitle(bookId3, "en"));
Observable<List<JsonObject>> observable = Observable.zip(calls, responses -> {
// merge responses, return List
...
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io());
observer = new DisposableObserver<List<JsonObject>> () {
#Override
public void onNext(List<JsonObject> result) { // got all API results }
#Override
public void onError(Throwable e) { }
#Override
public void onComplete() { }
};
observable.subscribe(observer);
Using RxJava is the easy way of merging Retrofit Calls. Merging Calls manually by enqueuing all Calls and doing something when all of them invoke onResponse, will probably be more complex than simply using Observable.zip(...).
The other choice that you have is using Kotlin coroutines (now Retrofit has out of the box support for them). But that depends on the Kotlin presence in your code and your willingness of using coroutines.
EDIT:
(Answering your question from the comment)
If you really think about Calls and RxJava Observables you don't really have to do anything more when using RxJava. When using raw Calls you still have to:
Make sure you're on the right thread if you want to touch Views (observeOn(AndroidSchedulers.mainThread()))
Make sure you're touching network on the right thread (subscribeOn(Schedulers.io()))
Make sure you're not using the response when your Activity/Fragment/Something else is no longer present (disposing of the Disposable in RxJava handles that)
You can significantly simplify your example:
Don't create Observable & Observer. Simply use the subscribe method which returns Disposable. And then maintain just this one Disposable.
You probably don't need onComplete so you can use the simpler version of .subscribe(...)
You can remove the need for .subscribeOn(Schedulers.io()) by properly creating your RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()) when building the Retrofit instance.
BooksApi booksApi = ApiProvider.getBooksAPI();
List<Observable<JsonObject>> mergedCalls = new ArrayList<>();
mergedCalls.add(booksApi.getBookTitle(bookId1, "en"));
mergedCalls.add(booksApi.getBookTitle(bookId2, "en"));
mergedCalls.add(booksApi.getBookTitle(bookId3, "en"));
final Disposable disposable = Observable
.zip(mergedCalls, responses -> {
// merge responses, return List
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(list -> {
// got all API results
}, throwable -> {
});
Doing that for one call would be as simple as:
final Disposable disposable = booksApi
.getBookTitle(bookId1, "en")
.observeOn(AndroidSchedulers.mainThread())
.subscribe(title -> {
// got the result
}, throwable -> {
});

How to make sure the rx java observable is not run again until the first call finishes

We have a sync in a sync adapter. Once the sync is called we need to block any other calls, from the OS, to that sync adapter until we have finished handling the response from the first sync. I thought that calling .blockingGet() on a Single would do the trick, but it doesn't. How can I make sure that all code being handled in the Single finishes before we allow the Single to be called/subscribed to again?
#Override
public void onPerformTrackedSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
try {
mCurrentUpdater = createUpdater();
mCurrentUpdater
.update()
.blockingGet();
onSyncCompleted(null);
} catch (Exception ex) {
onSyncCompleted(ex);
onError(ex);
}
}
#NonNull
public final Single<Complete> update() {
synchronized (this) {
if (mUpdateCalled) throw new IllegalStateException("Updaters should not be reused");
mUpdateCalled = true;
}
updateOnePage(); // will call onUpdaterPageSuccess which can continue updating
return mCompleteSubject
.take(1)
.singleOrError();
}
/**
* Update an individual "page" of data. Calls to onUpdaterPageSuccess may call back into this function.
*/
void updateOnePage() {
updateNextPage()
.subscribe(
this::onUpdaterPageSuccess,
this::onPageFailure
);
}
This is were the actual network sync is called:
protected Single<Page> updateNextPage() {
runMigrations();
String syncMarker = AreaSyncService.getSyncMarker();
Log.i("AAAZ", "Area Sync Requested " + Thread.currentThread());
return buildRequestData(new Date())
.flatMap(requestData -> mSalesRabbitService.syncAreas(syncMarker,
requestData.syncedAreas,
requestData.syncedAreaUserHistories,
requestData.deletedAreaIds,
requestData.deletedAreaUserHistoryIds)
.map(areaSyncValidated -> Pair.create(areaSyncValidated, requestData)))
.map(pair -> new Page(pair.first, pair.second))
.flatMap(this::updateWithPage);
}
And here we handle the sync response with another Single
protected Single<Page> updateWithPage(#NonNull Page page) {
Log.i("AAAZ", "Area Sync Requested " + Thread.currentThread());
return Single.just(page)
.flatMap(this::updateIdTrackersAndLastAreaSyncDevice)
.flatMap(this::addAreaIdsToNewAreas)
.flatMap(this::addAreaUserHistoryIdsToNewAreaUserHistories)
.flatMap(this::updateAreas)
.flatMap(this::updateHistories)
.flatMap(this::createAreas)
.flatMap(this::createHistories)
.flatMap(this::resetCacheFirstTime)
.flatMap(this::postChangedEvents)
.flatMap(this::handleDeletionsResetCachePostEvent)
.flatMap(this::housekeepingIfApplicable);
}
I suspect the issue is something I'm doing wrong between the update() method and the updateOnePage() method. How can I make sure that the single subscribed() to in updateOnePage() finishes completely before it can be called again?
As part of your observer chain, where the queries are being constructed, at the end of updateNextPage(), you have a flatMap() operator that invokes updateOnePage(). flatMap() takes an additional argument which is the number of parallel operations. Add the parameter 1.
.flatMap(this::updateWithPage, 1);

RxJava Chained Observables and NetworkMainThreadException

So I have this code:
public Observable<AbstractXMPPConnection> connect(final AbstractXMPPConnection connection) {
return Observable.<AbstractXMPPConnection>create(subscriber -> {
try {
AbstractXMPPConnection connection2 = connection.connect();
if (connection2.isConnected()) {
subscriber.onNext(connection2);
subscriber.onCompleted();
}
} catch (SmackException | IOException | XMPPException e) {
e.printStackTrace();
subscriber.onError(e);
}
})
.doOnError(throwable -> LOGI("111", "Connection OnError called"));
}
public Observable<AbstractXMPPConnection> connectWithRetry(final AbstractXMPPConnection connection) {
return connect(connection)
.retryWhen(attempts -> attempts.zipWith(Observable.range(1, MAX_CONNECTION_TRIES), (throwable, integer) -> new Pair<>(throwable, integer))
.flatMap(pair -> {
if (pair.second == MAX_LOGIN_TRIES)
return Observable.error(pair.first);
return Observable.timer(pair.second, TimeUnit.SECONDS);
}));
}
public void connect() {
assertTrue("To start a connection to the server, you must first call init() method!",
this.connectionConfig != null);
connectionHelper.connectWithRetry(connection)
.observeOn(Schedulers.newThread())
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<AbstractXMPPConnection>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
LOGI(TAG, "ConnectionHelper Connection onError\n");
/**{#link LoginActivity#onConnectionFailure(OnConnectionFailureEvent)} */
MainApplication.getInstance().getBusInstance().post(new OnConnectionFailureEvent());
}
#Override
public void onNext(AbstractXMPPConnection connection) {
LOGI(TAG, "ConnectionHelper Connection onNext");
// onConnected();
}
});
}
I have some questions about chaining observables. Imagining this scenario, in which I have a connect Observable, which sometimes I use, but I use mainly the connectWithRetry() Observable.
My question is, what would happen if a added this:
.observeOn(Schedulers.newThread())
.subscribeOn(AndroidSchedulers.mainThread())
To both the connect() and connectWithRetry()? In this scenario, when I call
public void connect and specify a scheduler, the previous ones are ignored?
And why am I getting NetworkOnMainThreadException? The explicit observeOn(Schedulers.newThread()) is there, it shouldnt be giving me that error
I'll address your NetworkOnMainThread issue first.
observeOn(Schedulers.newThread()) means the output will be observed on a new thread - that is, the code in your subscriber (onComplete/Error/Next) will be run on that thread.
subscribeOn(AndroidSchedulers.mainThread() means subscription will happen on the main thread - the code in your created observable (connection.connect() etc) is what is run when subscription happens.
So simply swap the schedulers:
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
So to address your first question, they're not ignored, they're just being used incorrectly. Hopefully from this you can see what would happen if you moved similar calls in to the chain inside your methods that return observables: nothing different to what you've already done. The calls would simply be in a different place.
So where to put the scheduler selection? That's up to you. You may gain increased clarity by not having the subscribeOn call inside the methods for creating your observables:
connectionHelper.connectWithRetry(connection)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
However, if you feel like you're calling this everywhere for no reason, you can instead move the subscribeOn call inside your methods:
return connect(connection)
.retryWhen(...)
.flatMap(...)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
Note that these don't have to be bundled up together like this - you could subscribeOn inside your method, but leave observeOn up to any callers that want their results on a specific thread.
Please try Schedulers.io() may be issue resolve.

Android: Polling a server with Retrofit

I'm building a 2 Player game on Android. The game works turnwise, so player 1 waits until player 2 made his input and vice versa. I have a webserver where I run an API with the Slim Framework. On the clients I use Retrofit. So on the clients I would like to poll my webserver (I know it's not the best approach) every X seconds to check whether there was an input from player 2 or not, if yes change UI (the gameboard).
Dealing with Retrofit I came across RxJava. My problem is to figure out whether I need to use RxJava or not? If yes, are there any really simple examples for polling with retrofit? (Since I send only a couple of key/value pairs) And if not how to do it with retrofit instead?
I found this thread here but it didn't help me too because I still don't know if I need Retrofit + RxJava at all, are there maybe easier ways?
Let's say the interface you defined for Retrofit contains a method like this:
public Observable<GameState> loadGameState(#Query("id") String gameId);
Retrofit methods can be defined in one of three ways:
1.) a simple synchronous one:
public GameState loadGameState(#Query("id") String gameId);
2.) one that take a Callback for asynchronous handling:
public void loadGameState(#Query("id") String gameId, Callback<GameState> callback);
3.) and the one that returns an rxjava Observable, see above. I think if you are going to use Retrofit in conjunction with rxjava it makes the most sense to use this version.
That way you could just use the Observable for a single request directly like this:
mApiService.loadGameState(mGameId)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<GameState>() {
#Override
public void onNext(GameState gameState) {
// use the current game state here
}
// onError and onCompleted are also here
});
If you want to repeatedly poll the server using you can provide the "pulse" using versions of timer() or interval():
Observable.timer(0, 2000, TimeUnit.MILLISECONDS)
.flatMap(mApiService.loadGameState(mGameId))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<GameState>() {
#Override
public void onNext(GameState gameState) {
// use the current game state here
}
// onError and onCompleted are also here
}).
It is important to note that I am using flatMap here instead of map - that's because the return value of loadGameState(mGameId) is itself an Observable.
But the version you are using in your update should work too:
Observable.interval(2, TimeUnit.SECONDS, Schedulers.io())
.map(tick -> Api.ReceiveGameTurn())
.doOnError(err -> Log.e("Polling", "Error retrieving messages" + err))
.retry()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(sub);
That is, if ReceiveGameTurn() is defined synchronously like my 1.) above, you would use map instead of flatMap.
In both cases the onNext of your Subscriber would be called every two seconds with the latest game state from the server. You can process them one after another of limit the emission to a single item by inserting take(1) before subscribe().
However, regarding the first version: A single network error would be first delivered to onError and then the Observable would stop emitting any more items, rendering your Subscriber useless and without input (remember, onError can only be called once). To work around this you could use any of the onError* methods of rxjava to "redirect" the failure to onNext.
For example:
Observable.timer(0, 2000, TimeUnit.MILLISECONDS)
.flatMap(new Func1<Long, Observable<GameState>>(){
#Override
public Observable<GameState> call(Long tick) {
return mApiService.loadGameState(mGameId)
.doOnError(err -> Log.e("Polling", "Error retrieving messages" + err))
.onErrorResumeNext(new Func1<Throwable, Observable<GameState>(){
#Override
public Observable<GameState> call(Throwable throwable) {
return Observable.emtpy());
}
});
}
})
.filter(/* check if it is a valid new game state */)
.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<GameState>() {
#Override
public void onNext(GameState gameState) {
// use the current game state here
}
// onError and onCompleted are also here
}).
This will every two seconds:
* use Retrofit to get the current game state from the server
* filter out invalid ones
* take the first valid one
* and the unsubscribe
In case of an error:
* it will print an error message in doOnNext
* and otherwise ignore the error: onErrorResumeNext will "consume" the onError-Event (i.e. your Subscriber's onError will not be called) and replaces it with nothing (Observable.empty()).
And, regarding the second version: In case of a network error retry would resubscribe to the interval immediately - and since interval emits the first Integer immediately upon subscription the next request would be sent immediately, too - and not after 3 seconds as you probably want...
Final note: Also, if your game state is quite large, you could also first just poll the server to ask whether a new state is available and only in case of a positive answer reload the new game state.
If you need more elaborate examples, please ask.
UPDATE: I've rewritten parts of this post and added more information in between.
UPDATE 2: I've added a full example of error handling with onErrorResumeNext.
Thank you, I finally made it in a similar way based the post I referred to in my question. Here's my code for now:
Subscriber sub = new Subscriber<Long>() {
#Override
public void onNext(Long _EmittedNumber)
{
GameTurn Turn = Api.ReceiveGameTurn(mGameInfo.GetGameID(), mGameInfo.GetPlayerOneID());
Log.d("Polling", "onNext: GameID - " + Turn.GetGameID());
}
#Override
public void onCompleted() {
Log.d("Polling", "Completed!");
}
#Override
public void onError(Throwable e) {
Log.d("Polling", "Error: " + e);
}
};
Observable.interval(3, TimeUnit.SECONDS, Schedulers.io())
// .map(tick -> Api.ReceiveGameTurn())
// .doOnError(err -> Log.e("Polling", "Error retrieving messages" + err))
.retry()
.subscribe(sub);
The problem now is that I need to terminate emitting when I get a positive answer (a GameTurn). I read about the takeUntil method where I would need to pass another Observable which would emit something once which would trigger the termination of my polling. But I'm not sure how to implement this.
According to your solution, your API method returns an Observable like it is shown on the Retrofit website. Maybe this is the solution? So how would it work?
UPDATE:
I considered #david.miholas advices and tried his suggestion with retry and filter. Below you can find the code for the game initialization. The polling should work identically: Player1 starts a new game -> polls for opponent, Player2 joins the game -> server sends to Player1 opponent's ID -> polling terminated.
Subscriber sub = new Subscriber<String>() {
#Override
public void onNext(String _SearchOpponentResult) {}
#Override
public void onCompleted() {
Log.d("Polling", "Completed!");
}
#Override
public void onError(Throwable e) {
Log.d("Polling", "Error: " + e);
}
};
Observable.interval(3, TimeUnit.SECONDS, Schedulers.io())
.map(tick -> mApiService.SearchForOpponent(mGameInfo.GetGameID()))
.doOnError(err -> Log.e("Polling", "Error retrieving messages: " + err))
.retry()
.filter(new Func1<String, Boolean>()
{
#Override
public Boolean call(String _SearchOpponentResult)
{
Boolean OpponentExists;
if (_SearchOpponentResult != "0")
{
Log.e("Polling", "Filter " + _SearchOpponentResult);
OpponentExists = true;
}
else
{
OpponentExists = false;
}
return OpponentExists;
}
})
.take(1)
.subscribe(sub);
The emission is correct, however I get this log message on every emit:
E/Pollingļ¹• Error retrieving messages: java.lang.NullPointerException
Apperently doOnError is triggered on every emit. Normally I would get some Retrofit debug logs on every emit which means that mApiService.SearchForOpponent won't get called. What do I do wrong?

How to wait multiple nested async calls by using of RxJava-Android?

I'm new to RxJava, here's my case,
send request A and will get List<A> back
for each A, send request AA and will get AA back, bind A and AA then
there is B & BB with similar logic
do something only after all requests complete
Example:
request(url1, callback(List<A> listA) {
for (A a : listA) {
request(url2, callback(AA aa) {
a.set(aa);
}
}
}
A and B are independent
How to structure the code? I also used Retrofit as network client.
OK, I think this should solve the first part of your problem:
Notice that the second call to flatMap is given 2 arguments - there is a version of flatMap that not only produces an Observable for each input item but that also take a second function which in turn will combine each item from the resulting Observable with the corresponding input item.
Have a look at the third graphic under this heading to get an intuitive understanding:
https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables#flatmap-concatmap-and-flatmapiterable
Observable<A> obeservableOfAs = retrofitClient.getListOfAs()
.flatMap(new Func1<List<A>, Observable<A>>() {
#Override
public Observable<A> call(List<A> listOfAs) {
return Observable.from(listOfAs);
}
)}
.flatMap(new Func1<A, Observable<AA>>() {
#Override
public Observable<AA> call(A someA) {
return retrofitClient.getTheAaForMyA(someA);
}
},
new Func2<A, AA, A>() {
#Override
public A call(A someA, AA theAaforMyA) {
return someA.set(theAaforMyA);
}
})
...
From here on I am still not sure how you want to continue: Are you ready to just subscribe to the resulting Observable of As? That way you could handle each of the As (onNext) or just wait until all are done (onCompleted).
ADDENDUM: To collect all Items into a single List at the end, that is turn your Observable<A> into an Observable<List<A>> use toList().
https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#tolist
So you have:
Observable<List<A>> observableOfListOfAs = observableOfAs.toList();
If you need more fine grained control over the construction of your list, you can also use reduce.
https://github.com/ReactiveX/RxJava/wiki/Mathematical-and-Aggregate-Operators#reduce
For the Bs, simply duplicate the whole flow you used for the As.
You can then use zip to wait for both flows to complete:
Observable.zip(
observableOfListOfAs,
observableOfListOfBs,
new Func2<List<A>, List<B>, MyPairOfLists>() {
#Override
public MyPairOfLists call(List<A> as, List<B> bs) {
return new MyPairOfLists(as, bs);
}
}
)
.subscribe(new Subscriber<MyPairOfLists>() {
// onError() and onCompleted() are omitted here
#Override
public void onNext(MyPairOfLists pair) {
// now both the as and the bs are ready to use:
List<A> as = pair.getAs();
List<B> bs = pair.getBs();
// do something here!
}
});
I suppose you can guess the definition of MyPairOfLists.

Categories

Resources