使用RxJava在单个线程上链接请求

时间:2016-09-13 06:10:06

标签: android rx-java retrofit2 rx-android

我将用户的位置保存在应用本地数据库中,然后将其发送到服务器。服务器返回成功后,我会删除已发送的位置。

每次在数据库中保存一个点时,我都会调用此方法:

public void sendPoint(){
    amazonRetrofit.postAmazonPoints(databaseHelper.getPoints())
            .map(listIdsSent -> deleteDatabasePoints(listIdsSent))
            .doOnCompleted(() -> emitStoreChange(finalEvent))
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(AndroidSchedulers.from(backgroundLooper))
            .subscribe();
}
  1. 我在数据库中查询要发送到服务器的点
  2. 我从服务器收到了成功发送的点列表
  3. 使用.map(),我收集成功发送的点并从本地数据库中删除它们
  4. 有时,我会反复调用此方法,而不必等待先前的请求完成并删除发送的点。因此,当我再次调用该方法时,它会发布与前一个请求相同的点,因为之前的请求尚未完成,因此尚未使用.map()删除该点。导致服务器接收重复...

    时间轴

    • 第一次致电postPoint()
    • 从数据库
    • 中搜索 A,B,C
    • 发布 A,B,C 指向服务器
    • 第二次致电postPoint()
    • 从数据库
    • 中搜索 A,B,C,D
    • 发布 A,B,C,D 指向服务器
    • 从第一次请求获得成功
    • 从本地数据库中删除 A,B,C
    • 从第二次请求获得成功
    • 从本地数据库中删除 A,B,C,D

    结果:

    服务器数据库现已收到: A,B,C,A,B,C,D

    每个请求都是按顺序发生的,但是当我过快地调用sendPoint()时,会以某种方式将相同的位置点发送到服务器。我该如何解决这个问题?

3 个答案:

答案 0 :(得分:3)

首先,你没有正确使用observerOn运算符,一旦定义了,就会对管道中的步骤应用observeOn运算符。 因此,如果您在subscribeOn之前的管道末尾定义,那么您之前的所有步骤都不会在该线程中执行。

此外,由于您需要等到服务器调用的响应,您可以使用Subscriber已经提供的回调处理程序(onNext(),onComplete())

 public void sendPoint(){
    Observable.from(databaseHelper.getPoints())
              .observeOn(AndroidSchedulers.mainThread())
              .flatMap(poins-> amazonRetrofit.postAmazonPoints(points))
              .subscribeOn(AndroidSchedulers.from(backgroundLooper))
              .subscribe(listIdsSent-> deleteDatabasePoints(listIdsSent), () -> emitStoreChange(finalEvent));
}

如果您想查看ObserverOn和SubscribeOn的更多示例,可以在这里查看。 https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/scheduler/ObservableAsynchronous.java

答案 1 :(得分:1)

您应该在客户端或/和后端进行某种验证。

客户方:
最简单的解决方案是在表格中添加两列,其中包括" processing"和"上传"。 从数据库和clausure where processing=false and uploaded=false中选择位置时。 然后,当您准备好发送集合processing=true的行时,以及当服务器返回成功集done=true时。

后端(可选,取决于要求):
您应该将带有时间戳的位置发送到服务器(可能是客户端表中的另外一列)。如果服务器获得的时间戳早于数据库中的最后一个位置,则它不应该存储它。

RxJava解决方案:
您可以使用内存缓存实现类似的解决方案,该缓存在所有sendPoint周围保留为List

伪代码:

public void sendPoint(){
      databaseHelper.getPoints()
            .filter(points -> pointsNotInCache())
            .map(points -> amazonRetrofit.postAmazonPoints())
            .map(points -> addToCache()) 
            .map(listIdsSent -> deleteDatabasePoints(listIdsSent))
            .map(listIdsSent -> removeSentPointsFromCache()) //if you would like save memory
            .doOnCompleted(() -> emitStoreChange(finalEvent))
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(AndroidSchedulers.from(backgroundLooper))
            .subscribe();
}

答案 2 :(得分:1)

看起来像其他人一样,你需要一个中间缓存。

HashSet<Point> mHashSet = new HashSet<>();

public void sendPoint() {
    Observable.from(databaseHelper.getPoints())
        .filter(point -> !mHashSet.contains(point))
        .doOnNext(mHashSet::put)
        .toList()
        .flatMap(amazonRetrofit::postAmazonPoints)
        .map(this::deleteDatabasePoints)
        .doOnCompleted(() -> emitStoreChange(finalEvent))
        .observeOn(AndroidSchedulers.mainThread())
        .subscribeOn(AndroidSchedulers.from(backgroundLooper))
        .subscribe();
}