使RxJava异步任务线程安全

时间:2016-09-12 18:28:23

标签: android multithreading rx-java retrofit2

在我的应用中,我有一项跟踪用户位置的服务,然后使用RxJava将其发送到服务器。如果请求成功,我将收回插入的ID,然后我可以从本地数据库中删除它们。

  1. 在数据库中查询要发送的点数
  2. 如果不为空,我会从数据库中发布所有收集的点
  3. 如果请求成功,我会从数据库中删除已发布的点数
  4. 我的问题是,我在上一个任务结束之前快速调用了observable,因此服务器会收到重复点(两个请求的点相同)。我需要在单个线程上执行Observable以避免在上一个数据库结束之前有另一个任务查询数据库。我创建了一个Looper主题,但我仍然发送重复项,我不知道为什么。现在服务器请求似乎要等到它在执行下一个请求之前结束,但是在下一个请求时,它仍然发送相同的点! Gahh

        final StoreChangeEvent finalEvent = event;
        Observable
                .defer(() -> Observable.just(database.getAllPoints()))
                .flatMap(pointsList -> (pointsList.isEmpty()) ? Observable.empty() : amazonRetrofit.postAmazonPoints(pointsList)
                        .map(result -> deletePoint(result))
                        .doOnError(error -> emitStoreChange(new ErrorMessageEvent(error.getMessage())))
                        .doOnCompleted(() -> emitStoreChange(finalEvent))
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribeOn(AndroidSchedulers.from(backgroundLooper)))
                .subscribe();
    

    似乎database.getAllPoints()太快被调用了...我应该添加.blocking()吗?

    我们说我有5点要发布到服务器( A,B,C,D

    1. 我查询数据库并将A-B-C-D发送到服务器
    2. 我从设备中获得了另一点(点 E
    3. 我查询数据库并发送( A-B-C-D-E
    4. 我从服务器获得成功,然后从本地数据库中删除A-B-C-D
    5. 我从第二个请求获得成功,然后从本地数据库中删除A-B-C-D-E
    6. 结果:A-B-C-D在服务器数据库上有两次,因为两个请求是以相同的点发送的

1 个答案:

答案 0 :(得分:2)

选项1 - 主题

您可以尝试使用Subject - 既可作为观察者又可作为观察者的事物。

订阅一次,并在其他地方通知新事件(onNext())。订户将随后处理事件。

我使用了SerializedSubject,以防您从不同的主题中调用notifyNewEvent(),否则您可以使用BehaviourSubject

SerializedSubject<StoreChangeEvent, StoreChangeEvent> subject = new SerializedSubject(BehaviorSubject.create())

public void initialize() {
    // Since you access your incoming event from doOnCompleted,
    // need this extra flatMap function so that you can access your event
    // outside rx java chain.
    subject.flatMap(new Func1() {
        @Override
        public Observable call(StoreChangeEvent event) {
            return Observable
                    .just(database.getAllPoints())
                    .flatMap(pointsList -> (pointsList.isEmpty()) ? Observable.empty() : amazonRetrofit.postAmazonPoints(pointsList)
                            .map(result -> deletePoint(result))
                            .doOnError(error -> emitStoreChange(new ErrorMessageEvent(error.getMessage())))
                            .doOnCompleted(() -> emitStoreChange(finalEvent)));
        }
    })
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(AndroidSchedulers.from(backgroundLooper))
            .subscribe();
}

public void notifyNewEvent(StoreChangeEvent event) {
    subject.onNext(event);
}

选项2 - 执行者

如果您不访问用户界面,为什么还要打扰主题并观察,请订阅。使用一个线程创建一个执行程序(随后执行所有任务),并将任务提交给它,并在那里利用RxJava有用的功能。

ExecutorService executorService = Executors.newSingleThreadExecutor();

public void notifyNewEvent(final StoreChangeEvent event) {
    executorService.execute(new Runnable() {
        public void run() {
            Observable.just(database.getAllPoints())
                // Blocking, so that the thread doesn't exit
                // and blocks on subscribe() till completion.
                .toBlocking() 
                .flatMap(pointsList -> (pointsList.isEmpty()) ? Observable.empty() : amazonRetrofit.postAmazonPoints(pointsList)
                    .map(result -> deletePoint(result))
                    .doOnError(error -> emitStoreChange(new ErrorMessageEvent(error.getMessage())))
                    .doOnCompleted(() -> emitStoreChange(finalEvent)))
                .subscribe();
        }
    });
}