我将用户的位置保存在应用本地数据库中,然后将其发送到服务器。服务器返回成功后,我会删除已发送的位置。
每次在数据库中保存一个点时,我都会调用此方法:
public void sendPoint(){
amazonRetrofit.postAmazonPoints(databaseHelper.getPoints())
.map(listIdsSent -> deleteDatabasePoints(listIdsSent))
.doOnCompleted(() -> emitStoreChange(finalEvent))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(AndroidSchedulers.from(backgroundLooper))
.subscribe();
}
.map()
,我收集成功发送的点并从本地数据库中删除它们有时,我会反复调用此方法,而不必等待先前的请求完成并删除发送的点。因此,当我再次调用该方法时,它会发布与前一个请求相同的点,因为之前的请求尚未完成,因此尚未使用.map()
删除该点。导致服务器接收重复...
时间轴
postPoint()
postPoint()
结果:
服务器数据库现已收到: A,B,C,A,B,C,D
每个请求都是按顺序发生的,但是当我过快地调用sendPoint()
时,会以某种方式将相同的位置点发送到服务器。我该如何解决这个问题?
答案 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();
}