Android:使用 Retrofit 轮询服务器

标签 android rest polling retrofit rx-java

我正在 Android 上构建一个 2 人游戏。游戏按顺序进行,因此玩家 1 等待直到玩家 2 做出输入,反之亦然。我有一个网络服务器,我在其中使用 Slim 运行 API框架。在我使用 Retrofit 的客户端上。因此,在客户端上,我想每隔 X 秒轮询一次我的网络服务器(我知道这不是最好的方法),以检查是否有来自玩家 2 的输入,如果是,则更改 UI(游戏板)。

处理 Retrofit 我遇到了 RxJava。我的问题是弄清楚我是否需​​要使用 RxJava?如果是,是否有任何真正简单的改造轮询示例? (因为我只发送了几个键/值对)如果不是如何用改造来代替呢?

我找到了 this线程在这里,但它对我也没有帮助,因为我仍然不知道我是否需要 Retrofit + RxJava,是否有更简单的方法?

最佳答案

假设您为 Retrofit 定义的接口(interface)包含如下方法:

public Observable<GameState> loadGameState(@Query("id") String gameId);

改造方法可以通过以下三种方式之一定义:

1.) 一个简单的同步:

public GameState loadGameState(@Query("id") String gameId);

2.) 采用Callback 进行异步处理的:

public void loadGameState(@Query("id") String gameId, Callback<GameState> callback);

3.) 和返回 rxjava Observable 的那个,见上文。我认为,如果您打算将 Retrofit 与 rxjava 结合使用,那么使用此版本最有意义。

这样你就可以像这样直接对单个请求使用 Observable:

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
});

如果您想重复轮询服务器,您可以使用 timer()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
}).

重要的是要注意我在这里使用 flatMap 而不是 map - 这是因为 loadGameState(mGameId) 的返回值是本身是一个 Observable

但是您在更新中使用的版本也应该有效:

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);

也就是说,如果 ReceiveGameTurn() 像我上面的 1.) 那样同步定义,您将使用 map 而不是 flatMap

在这两种情况下,您的SubscriberonNext 将每两秒调用一次,并使用来自服务器的最新游戏状态。您可以通过在 subscribe() 之前插入 take(1) 来一个接一个地处理它们,或者将发射限制为单个项目。

然而,关于第一个版本:单个网络错误将首先传递给 onError 然后 Observable 将停止发射任何更多的项目,使您的订阅者无用且没有输入(记住,onError 只能调用一次)。要解决此问题,您可以使用 rxjava 的任何 onError* 方法将失败“重定向”到 onNext。

例如:

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
}).

这将每两秒: * 使用 Retrofit 从服务器获取当前游戏状态 * 过滤掉无效的 * 取第一个有效的 * 和退订

如果出现错误: * 它将在 doOnNext 中打印一条错误消息 * 否则忽略错误:onErrorResumeNext 将“消耗”onError-Event(即您的订阅者onError 将不会被调用)并将其替换为任何内容(Observable.empty())。

并且,关于第二个版本:如果出现网络错误,retry 将立即重新订阅间隔 - 因为 interval 在订阅下一个时立即发出第一个 Integer请求也将立即发送 - 而不是您可能想要的 3 秒后......

最后说明:此外,如果您的游戏状态非常大,您也可以先轮询服务器以询问是否有新状态可用,只有在肯定回答的情况下才重新加载新游戏状态。

如果您需要更详细的示例,请询问。

更新:我重写了这篇博文的部分内容并在其间添加了更多信息。

更新 2:我添加了使用 onErrorResumeNext 进行错误处理的完整示例。

关于Android:使用 Retrofit 轮询服务器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28369689/

相关文章:

android - MediaMetadataRetriever setdatasource 出错

php - CakePHP 3 REST API + CORS 请求和 OPTIONS 方法

c++ - poll with recv、epoll with recv 和 simple recv 之间的性能差异?

android - android ui组件的事件总线和生命周期

Android 应用程序未安装在设备上?

rest - 架构 REST : How do I design a REST API for request+approval, 2 个资源还是 1 个?

java - 如何使用 ServletContainerInitializer 映射 REST Web 服务?

javascript - 使用 Axios 和 Promises 循环 API 调用

linux - 获取上次挂载/卸载的文件系统的名称

android - 给出 Intent 返回的结果代码或数据是什么