RXJava条件执行Observables + null处理

时间:2018-01-19 12:21:28

标签: android caching functional-programming reactive-programming rx-java2

我是整个函数式编程和反应式概念的新手,并试图解决以下问题。

我有一个API-Client,我正在使用Retrofit。 还有一个本地数据库,它充当API响应的持久缓存。

我想要实现的是这样的事情:

  1. 从本地数据库加载对象
  2. 如果没有对象或数据库返回null对象:
    • 执行API请求并从在线来源获取数据
    • 之后,坚持收到数据并返回持久数据
  3. 如果从本地数据库返回了对象,请检查是否需要在线更新
    • 需要在线更新(在线获取数据,保留并返回持久对象)
    • 不需要在线更新(返回本地数据)
  4. 我想出的是以下内容:

    defaultIfEmpty

    }

    我遇到的问题是,如果数据库返回null,我的订阅回调中不会返回任何对象。 我已经尝试了 pwdStrength.SetPassword(text_passmysql.Text); pwdStrength1.SetPassword(text_adminpass.Text); switch (pwdStrength.GetPasswordStrength() || pwdStrength1.GetPasswordStrength()) { case "Very Weak": case "Weak": // Show an error message to the user MessageBox.Show("A password do MySQL é muito fraca, escolha um mais forte.\nApenas pode alterar a password MySQL para uma mais forte, caso contrário não será guardada.", "Configurações do Programa", MessageBoxButtons.OK, MessageBoxIcon.Error); break; case "Good": case "Strong": case "Very Strong": // Password deemed strong enough, allow user to be added to database etc //do something break; } - 方法但得出的结论是,这也无济于事,因为它期望一个对象而不是一个可观察对象。

    任何想法,如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

您应该使用Flowables代替。无论如何, RxJava 2.x no longer accepts null values and will yield NullPointerException immediately or as a signal to downstream。如果你切换到Flowables,那么你可以使用像 .onErrorReturnItem(Collections.emptyList())这样的东西,它比它给你多少信息的空白要好。没有结果,而不是null可能意味着不同数量的东西。

答案 1 :(得分:0)

我对原始答案进行了一些重新评估,并得出结论,大多数现场检查网络响应/从网络获取数据的需求根本不需要。

首先,如果网络请求出现问题,则抛出异常,该异常将上升并由observable的onError-subscriber处理。

其次,还不需要检查请求是否成功,因为通过使用异常,只有在调用链中的下一步时才能成功。

而且第三,takeWhile的使用使事情变得更加复杂,因为它实际上是必需的。 我决定使用一个简单的flatMap-Lambda来解决这个问题,它在内部使用了非常简单的if语句。因此,我认为代码更具可读性和可理解性。

您可以在下面找到我的问题的最终解决方案:

package com.appenetic.fame.model.repository.remote;

import android.support.annotation.NonNull;

import com.annimon.stream.Optional;
import com.appenetic.fame.api.service.LocationService;
import com.appenetic.fame.api.service.LocationServiceQueryBuilder;
import com.appenetic.fame.model.LocationCollection;
import com.appenetic.fame.model.repository.local.LocalLocationCollectionRepository;

import org.joda.time.DateTime;
import org.joda.time.Interval;

import java.io.IOException;

import io.reactivex.Observable;

/**
 * Created by shel on 18.01.18.
 */
public class LocationCollectionRepository {
    private final static Integer fetchInterval = 30; //Minutes
    private final LocationService locationService;
    private final LocalLocationCollectionRepository localRepository;

    public LocationCollectionRepository(@NonNull LocationService locationService, @NonNull LocalLocationCollectionRepository localRepository) {
        this.locationService = locationService;
        this.localRepository = localRepository;
    }

    public Observable<LocationCollection> getLocationCollection() throws IOException {
        return localRepository.getLocationCollection()
                .flatMap(locationCollectionOptional -> {
                    if (shouldFetch(locationCollectionOptional)) {
                        return persistLocationCollection(fetchLocationCollection().blockingFirst());
                    }

                    return Observable.just(locationCollectionOptional.get());
                });
    }

    //================================================================================
    // Private methods
    //================================================================================

    private Observable<LocationCollection> fetchLocationCollection() throws IOException {
        return Observable.fromCallable(() -> {
            LocationServiceQueryBuilder queryBuilder = LocationServiceQueryBuilder.query();
            return queryBuilder.invoke(locationService).execute().body();
        });
    }

    private Observable<LocationCollection> persistLocationCollection(@NonNull LocationCollection locationCollection) {
        return localRepository.saveLocationCollection(locationCollection);
    }

    private boolean shouldFetch(@NonNull Optional<LocationCollection> locationCollection) {
        if (locationCollection.isPresent()) {
            Interval interval = new Interval(new DateTime(locationCollection.get().getTimestamp()), new DateTime());

            return locationCollection.get().getHashValue() == null || interval.toDuration().getStandardMinutes() > fetchInterval;
        } else {
            return true;
        }
    }
}