以反应方式改变状态
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
的映射运算符
问题
- 什么是更好的解决方案"reactive",我在这里缺少什么?
- 使用反应式方法是否有意义?
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 恢复主序列。
最近我开始 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
的映射运算符问题
- 什么是更好的解决方案"reactive",我在这里缺少什么?
- 使用反应式方法是否有意义?
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 恢复主序列。