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?
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:
The problem now is that I need to terminate emitting when I get a positive answer (a
GameTurn
). I read about thetakeUntil
method where I would need to pass anotherObservable
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 anObservable
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.
The emission is correct, however I get this log message on every emit:
Apperently
doOnError
is triggered on every emit. Normally I would get some Retrofit debug logs on every emit which means thatmApiService.SearchForOpponent
won't get called. What do I do wrong?Let's say the interface you defined for Retrofit contains a method like this:
Retrofit methods can be defined in one of three ways:
1.) a simple synchronous one:
2.) one that take a
Callback
for asynchronous handling: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:
If you want to repeatedly poll the server using you can provide the "pulse" using versions of
timer()
orinterval()
:It is important to note that I am using
flatMap
here instead ofmap
- that's because the return value ofloadGameState(mGameId)
is itself anObservable
.But the version you are using in your update should work too:
That is, if
ReceiveGameTurn()
is defined synchronously like my 1.) above, you would usemap
instead offlatMap
.In both cases the
onNext
of yourSubscriber
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 insertingtake(1)
beforesubscribe()
.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 theonError*
methods of rxjava to "redirect" the failure to onNext.For example:
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" theonError
-Event (i.e. yourSubscriber
'sonError
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 sinceinterval
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
.