可选订阅者的Observable执行一次

时间:2016-10-20 02:58:03

标签: rx-java reactive-programming

无论订阅者如何,Observer如何使其逻辑执行一次(异步)。我的用例是Provider类,它通过委托给另一个对象(Model)来为SqliteOpenHelper类做CRUD。无论UI是否订阅,对ModelProvider.update的调用都应该保持对模型的更改。

class ModelProvider {
    Observable<String> updateModel(Model model) {
        return Observable.create((Subscriber<? super String> subscriber) -> {
            // synchronous for now, probably irrelevant
            String modelId = repository.save(model);
            subscriber.onNext(modelId);
        }).subscribeOn(Schedulers.io());
    }
}

class SomeActivity {
    void updateModel(Model model) {
        // dont care about the result, but need it to execute exactly once
        mModelProvider.updateModel(model);
    }
}

我正在将代码库移动到使用RxJava和反应模式,因此对repository.save(model)的调用是同步的,但是仍然需要逻辑在之后执行一次,无论是否有UI订阅者。< / p>

我考虑过简单地将存储库逻辑移出observable,然后返回一个使用该结果的不同observable:

class ModelProvider {
    Observable<String> updateModel(Model model) {
        String modelId = repository.save(model);

        return Observable.create((Subscriber<? super String> subscriber) -> {
            subscriber.onNext(modelId);
        }).subscribeOn(Schedulers.io());
    }
}

但这会强制IO在主线程上工作并阻止它。

另一个非解决方案:

    Observable<String> updateModel(Model model) {
        Observable<String> obs = Observable.create((Subscriber<? super String> subscriber) -> {
        String modelId = repository.save(model);
            subscriber.onNext(modelId);
        }).subscribeOn(Schedulers.io());

        obs.subscribe();

        return obs;
    }

这会在obs.subscribe()执行一次,但如果UI订阅它则会再次执行。

我刚刚学习反应模式,但我认为这是Subjects / Relays有用的地方。

2 个答案:

答案 0 :(得分:1)

这有效:

Observable<String> updateModel(Model model) {
    Observable<String> obs = Observable.create((Subscriber<? super String> subscriber) -> {
        String modelId = repository.save(model);
        subscriber.onNext(modelId);
    }).subscribeOn(Schedulers.io())

    // cached() returns a new observer over the original, which only executes once
    // but not until it is subscribed to
    Observable<String> cachedObs = obs.cache();
    // this "starts" execution of `repository.save()`
    cachedObs.subscribe();

    // return observable that executes once and caches (cachedObs)
    // returning the original (obs) would cause multiple executions
    return cachedObs;
}

创建后调用Observable.cache()会阻止重新调用,但需要调用obs.subscribe()才能获得 one-and-one-one 调用。

请注意,持久性块(repository.save()等...)在订阅时不会立即启动,而是排队在不确定点上的单独线程(Schedulers.io线程池)上异步执行在将来。

这使用rxjava-async-utils包并且也有效:

Observable<String> updateModel(Model model) {
    Observable<String> obs = Async.start(() -> repository.save(model));

    // cached() here is required in order to avoid the race condition
    Observable<String> cachedObs = obs.cache();

    return cachedObs;
}

请注意,需要调用cache()才能保存结果并将其提供给订阅者。没有它,就会出现一种竞争条件,即订阅者没有结果,因为Async.start块已经完成。

答案 1 :(得分:1)

正如@xst所说,但我更加简化:

return Observable
     .defer(() -> Observable.just(repository.save(model)))
     .subscribeOn(Schedulers.io())
     .cache();