使用RxJava2

时间:2016-12-19 17:38:58

标签: android retrofit rx-java reactive-programming rx-java2

我的应用中有一个由改装服务支持的数据层。 (到目前为止,只有网络上的持久性。当我进一步开发时,我将添加离线优先本地存储)

当我打电话给服务器时,Retrofit会向我返回Observable<List<Item>>。这很好用。在我的订阅者中,我在订阅时收到列表,然后可以使用Items填充我的UI。

我遇到的问题是:如果列表被修改(通过某种外部机制),我如何使observable重新查询改装服务并发出新的项目列表。我知道数据是陈旧的,但我不知道如何启动重新查询。

这是我的DataManager

的简化版本
class DataManager {

    // Retrofit
    RetrofitItemsService itemsService;

    // The observalble provided by retrofit
    Observable<List<Item>> itemsObservable;

    //ctor
    public DataManager(RetrofitItemsService itemsService) {
        this.itemsService = itemsService;
    }

    /* Creates and stores an observable if one has not been created yet.
     * Returns the observable so that it can be subscribed to by the function caller
     */
    public Observable<List<Item>> getItems(){
        if(itemsObservable == null){
            itemsObservable = itemsService.getItems();
        }

        return itemsObservable;
    }

    /* Adds a new Item to the list.
     */
    public Completable addItem(Item item){
        Completable call = itemsService.addItem(item);

        call.subscribe(()->{
            /*
             < < < Here > > >
             If someone has previously called getItems before this item was added, they now have stale data.

             How can I call something like:

             itemsObservable.refreshAllSubscribers()
            */
        });

        return call;
    }
}

2 个答案:

答案 0 :(得分:2)

您在这里遇到的问题在于 可观察的区别。有很多很棒的文章你可以谷歌来描述细节上的差异,所以让我来描述基础知识。

Cold observable为每个订阅者创建一个新的生产者。这意味着,当两个单独的订阅者订阅相同的冷可观察性时,他们每个都会收到这些排放的不同实例。它们可能(!)相等,但它们永远不会是不同的对象。在这里应用于您的案例,每个订阅者都有自己的生产者,该生产者向服务器请求数据并将其传递到流中。每个订阅者都有自己的制作人提供的数据。

Hot observable与其所有观察者共享一个制作人。如果生产者例如在对象集合中进行迭代,则在排放中间与第二个订阅者一起跳入意味着它将仅获得之后发出的项目(如果其未通过诸如replay之类的运算符进行修改)。任何订阅者收到的每个对象在所有观察者中也是同一个实例,因为它来自单个生成者。

所以从它的外观来看,你需要有一个热的observable来分发你的数据,所以当你知道它不再有效时,你只需通过这个热的观察者发出一次,每个观察者都会对更新感到满意。

幸运的是,将寒冷的观察结果变成热点通常不是什么大问题。您可以创建自己的生成器来模仿此行为,使用一个流行的运算符,如share,或者您只需转换流,使其行为像一个。

我建议使用PublishSubject刷新数据并将其与原始的冷可观察数据合并,如下所示:

class DataManager {

    .....

    PublishSubject<Boolean> refreshSubject = PublishSubject.create();

    // The observable for retrieving always fresh data
    Observable<List<Item>> itemsObservable;

    //ctor
    public DataManager(RetrofitItemsService itemsService) {
        this.itemsService = itemsService;
        itemsObservable = itemsService.getItems()
                              .mergeWith(refreshSubject.flatMap(refresh -> itemsService.getItems()))
    }


    public Observable<List<Item>> getItems(){
        return itemsObservable;
    }

    /* Adds a new Item to the list.
     */
    public Completable addItem(Item item){
        Completable call = itemsService.addItem(item);

        call.subscribe(()->{
            refreshSubject.onNext(true);
        });

        return call;
    }
}

答案 1 :(得分:1)

我想itemsService.getItems()会返回一个元素Observable,因此消费者无论如何都必须重新订阅以获取新数据,并且他们也会获得它,因为Retrofit Observables也会延迟/延迟。

你可以有一个单独的,#34;长&#34; Observable,在PublishSubject的帮助下,您可以在数据更改时触发:

final Subject<Object> onItemsChanged = PublishSubject.create().toSerialized();

public Observable<Object> itemsChanged() {
    return onItemsChanged;
}

public Completable addItem(Item item){
    Completable call = itemsService.addItem(item);

    // prevent triggering the addItem multiple times
    // Needs RxJava 2 Extensions library for now
    // as there is no Completable.cache() or equivalent in 2.0.3
    CompletableSubject cs = CompletableSubject.create();

    call.doOnComplete(() -> onItemsChanged.onNext("changed"))
    .subscribe(cs);

    return cs;
}