繁体   English   中英

将数据库和网络调用与RxJava2结合

[英]Combine database and network call with RxJava2

我有2个数据源:数据库(缓存)和api,我需要将它们组合成一个流。 我知道我可以简单地使用concatArray或类似的东西,但是我想实现更复杂的行为:

  • 可观测的流,最多将发射2个元素。

  • 一开始它将同时订阅这两个资源。

  • 如果api调用足够快(<〜300ms),它将仅从中发出数据并完成流。

  • 如果api调用会很慢(>〜300ms),请从数据库中发出数据,然后仍然等待api中的数据
  • 如果api调用无法成功,则从数据库发出数据并发出错误。
  • 如果数据库以某种方式比api慢,它将无法发出其数据(流完成解决了它)

我用以下代码完成了它:

    public Observable<Entity> getEntity() {
    final CompositeDisposable disposables = new CompositeDisposable();
    return Observable.<Entity>create(emitter -> {
        final Entity[] localEntity = new Entity[1];

        //database call:
        disposables.add(database.getEntity()
                .subscribeOn(schedulers.io())
                .doOnSuccess(entity -> localEntity[0] = entity) //saving our entity because 
                                                        //apiService can emit error before 300 ms 
                .delay(300, MILLISECONDS)
                .subscribe((entity, throwable) -> {
                    if (entity != null && !emitter.isDisposed()) {
                        emitter.onNext(entity);
                    }
                }));

        //network call:
        disposables.add(apiService.getEntity()
                .subscribeOn(schedulers.io())
                .onErrorResumeNext(throwable -> {
                    return Single.<Entity>error(throwable) //we will delay error here
                            .doOnError(throwable1 -> {
                                if (localEntity[0] != null) emitter.onNext(localEntity[0]); //api error, emit localEntity
                            })
                            .delay(200, MILLISECONDS, true); //to let it emit localEntity before emitting error
                })
                .subscribe(entity -> {
                    emitter.onNext(entity); 
                    emitter.onComplete(); //we got entity from api, so we can complete the stream
                }, emitter::onError));
    })
            .doOnDispose(disposables::clear)
            .subscribeOn(schedulers.io());
}

代码有点笨拙,我在这里在observable中创建了observables,我认为这很糟糕 但是通过这种方式,我可以全局访问发射器,这使我能够以自己想要的方式控制主流(发射数据,成功,错误)。

有更好的方法来实现这一目标吗? 我很想看看一些代码示例。 谢谢!

可能是下面的代码可以完成这项工作。 根据您的要求,我假设api和数据库处理Single<Entity>

private static final Object STOP = new Object();

public static void main(String[] args) {
    Database database = new Database(Single.just(new Entity("D1")));
    ApiService apiService = new ApiService(Single.just(new Entity("A1")));
    // ApiService apiService = new ApiService(Single.just(new Entity("A1")).delay(500, MILLISECONDS));
    // ApiService apiService = new ApiService(Single.error(new Exception("Error! Error!")));
    BehaviorSubject<Object> subject = BehaviorSubject.create();

    Observable.merge(
        apiService.getEntity()
                  .toObservable()
                  .doOnNext(t -> subject.onNext(STOP))
                  .doOnError(e -> subject.onNext(STOP))
                  .onErrorResumeNext(t ->
                                        Observable.concatDelayError(database.getEntity().toObservable(),
                                                                    Observable.error(t))),
        database.getEntity()
                .delay(300, MILLISECONDS)
                .toObservable()
                .takeUntil(subject)
    )
    .subscribe(System.out::println, 
               System.err::println);

    Observable.timer(1, MINUTES) // just for blocking the main thread
              .toBlocking()
              .subscribe();
}

由于出现以下情况,我无法删除对Subject的使用:“如果数据库以某种方式比api慢,它将无法发出其数据”和“如果api调用将是缓慢的(>〜300ms),则发出数据从数据库中,仍然等待api中的数据”。 否则, amb()运算符会很好用。

我希望这有帮助。

另一个解决方案可能是这个(无主题):

public static void main(String[] args) throws InterruptedException {
    Database database = new Database(Single.just(new Entity("D1")));
    ApiService apiService = new ApiService(Single.just(new Entity("A1")));
    // ApiService apiService = new ApiService(Single.just(new Entity("A1")).delay(400, MILLISECONDS));
    // ApiService apiService = new ApiService(Single.error(new Exception("Error! Error!")));

    database.getEntity()
            .toObservable()
            .groupJoin(apiService.getEntity()
                                 .toObservable()
                                 .onErrorResumeNext(
                                    err -> Observable.concatDelayError(database.getEntity().toObservable(),
                                                                       Observable.error(err))),
                       dbDuration -> Observable.timer(300, MILLISECONDS),
                       apiDuration -> Observable.never(),
                       (db, api) -> api.switchIfEmpty(Observable.just(db)))
            .flatMap(o -> o)
            .subscribe(System.out::println,
                       Throwable::printStackTrace,
                       () -> System.out.println("It's the end!"));

    Observable.timer(1, MINUTES) // just for blocking the main thread
              .toBlocking()
              .subscribe();
}

如果在300毫秒内没有从API服务发出任何信息( dbDuration -> timer(300, MILLISECONDS) ),则从数据库发出实体( api.switchIfEmpty(db) )。

如果api在300毫秒内发出某物,则仅发出其Entityapi.switchIfEmpty(.) )。

这似乎也像您希望的那样工作...

另一个更好的解决方案:

public static void main(String[] args) throws InterruptedException {
    Database database = new Database(Single.just(new Entity("D1")));
    ApiService apiService = new ApiService(Single.just(new Entity("A1")));
    // ApiService apiService = new ApiService(Single.just(new Entity("A1")).delay(400, MILLISECONDS));
    // ApiService apiService = new ApiService(Single.error(new Exception("Error! Error!")));

    Observable<Entity> apiServiceWithDbAsBackup =
            apiService.getEntity()
                      .toObservable()
                      .onErrorResumeNext(err -> 
                            Observable.concatDelayError(database.getEntity().toObservable(), Observable.error(err)));

    Observable.amb(database.getEntity()
                           .toObservable()
                           .delay(300, MILLISECONDS)
                           .concatWith(apiServiceWithDbAsBackup),
                   apiServiceWithDbAsBackup)
              .subscribe(System.out::println,
                         Throwable::printStackTrace,
                         () -> System.out.println("It's the end!"));

我们将amb()延迟使用,以使可观察的数据库采取将要发出的第一个数据库。 如果api服务出错,我们将从数据库中发出该项目。 这似乎也像您希望的那样工作...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM