以反应方式改变状态

State mutation in reactive way

最近我开始 android 项目,大量使用 Reactive extensions. I've read some introductions and tutorials, but I'm still at beginner's level. According to this 文章:

everything is a stream

然而我目前的理解(或心理障碍)告诉我任何改变状态的操作(例如从存储库中删除数据)不应该 be/return 和 stream/observable。

关于我的域的一些背景知识: 我有一个注册地理围栏的用例。自 geofences do not survive reboot 以来,我一直在跟踪存储库中的活动地理围栏。有时应用程序需要删除地理围栏,因此实现此目的的基本步骤是:

我目前的解决方案如下:

geofenceRepository.get(id)
            .map(new Func1<Geofence, String>() {
                @Override
                public String call(Geofence geofence) {
                    geofenceRepository.delete(geofence.getId()); // synchronous call here
                    return geofence.getRequestId();
                }
            })
            .toList()
            .flatMap(new Func1<List<String>, Observable<Status>>() {
                @Override
                public Observable<Status> call(List<String> ids) {
                    return locationProvider.removeGeofences(ids);
                }
            });

其中 Geofence 是我的自定义数据结构,而 locationProvider 来自 this nice library

您会注意到数据检索的实现方式与删除不同stream/observable。

在上面的例子中我不喜欢的是:带有 side effect

的映射运算符

问题

by reactive programming 我的意思是:

programming with asynchronous data streams

Reactive 很棒,我认为这种情况很完美。

我认为您在这里真正想做的是确保您的每个操作员都做 1 的事情。就像你说的,flatMap 也在移除你的地理围栏。

尝试在您的链中使用 onNext 运算符进行删除。你想要做什么它检索它,它看起来像 geofenceRepository.get(id),用运算符删除它,然后从 locationProvider 中删除它。可能是这样的:

geofenceRepository.get(id)
        .map(new Func1<Geofence, String>() {
            @Override
            public String call(Geofence geofence) {
                return geofence.getRequestId();
            }
        })
        .doOnNext(new Action1<String>){
          @Override
          public void call(final String geoFenceId) {
            geofenceRepository.delete(geofence.getId());
          }
        })
        .doOnNext(new Action1<String>() {
          @Override
          public void call(final String geoFenceId) {
                return locationProvider.removeGeofences(ids);
          }
        });

您可能真正想要做的是创建两个订阅者。这样,如果您想查看一个或两个的状态,您就可以。您可以结合每个的状态。这有点取决于从存储库中删除和从提供者中删除是否独立。

Observable<String> getFence = geofenceRepository.get(id)
        .map(new Func1<Geofence, String>() {
            @Override
            public String call(Geofence geofence) {
                return geofence.getRequestId();
            }
        });

        getFence.subscribe(new Action1<String>){
          @Override
          public void call(final String geoFenceId) {
            geofenceRepository.delete(geofence.getId());
          }
        });

        getFence.map(new Func1<String, Status>() {
          @Override
          public Status call(final String geoFenceId) {
                return locationProvider.removeGeofences(ids);
          }
        }).subscribe(new Action1<Status>(){
           @Override
           public void call(final Status status(){
              //Handle your status for each removal
           }
        });

我没有发现您的方法有任何问题,并且更具反应性意味着更多 API uses/returns 可观察对象。您可以在任何 lambda 中产生副作用,但在更改值时要小心,因为如果涉及异步,则同一对象可能会在管道的不同阶段同时发生更改。通常,我们使用不可变或有效不可变值来避免这个问题。没有真正需要拆分您的活动,因此建议的 doOnNext 分离是特定开发人员的偏好

如果您的 geofenceRepository.delete 有一个 return 是某种 Observable 的版本,您可以通过对其进行 flatMapping 来提高反应性:

get(id)
.flatMap(f -> geoFence.deleteAsync(f.getId()).map(v -> f.getRequestId()))
.toList()
.flatMap(...)
.subscribe(...)

在这里,deleteAsync 会 return 一个 Observable<Void> 完成后,将使用 requestId 恢复主序列。