使用RxJava Zip操作符合并含空值的Realm请求避免崩溃
解决RxJava2中Observable.zip处理Null结果导致崩溃的问题
咱们先明确崩溃的核心原因:RxJava 2.x 开始严格禁止流中出现 Null 值,所以当你的firstRepository.getUserData(userId)或secondRepository.getUserDetailData(userId)其中一个 Observable 发射了 null,就会触发你看到的NullPointerException。
下面给你几个实用的解决方案,你可以根据业务场景选择:
方案1:给可能返回Null的Observable设置默认空对象
如果某个数据源返回null是预期的正常情况,我们可以用defaultIfEmpty操作符给它提供一个空实例,确保流里不会出现null:
public void getDataFromTwoSources(DisposableObserver<ComplexUserDto> observer, String shopId) { Preconditions.checkNotNull(observer); // 给可能返回null的Observable添加默认空对象 Observable<UserData> userDataObservable = firstRepository.getUserData(userId) .defaultIfEmpty(new UserData()); // 根据需求创建空的UserData实例 Observable<UserDetailData> userDetailObservable = secondRepository.getUserDetailData(userId) .defaultIfEmpty(new UserDetailData()); // 同理处理UserDetailData final Observable<ComplexUserDto> observable = Observable.zip(userDataObservable, userDetailObservable, (userData, userDetails) -> { ComplexUserDto resultDto = new ComplexUserDto(userData, userDetails); return resultDto; }) .compose(rxTransformers.applyUnauthorizedHandler()) .compose(rxTransformers.applySchedulers()); addDisposable(observable.subscribeWith(observer)); }
注意:如果
UserData或UserDetailData是不可直接实例化的接口/抽象类,推荐用下面的Optional包装方案。
方案2:用Optional包装Nullable结果
如果不想创建空对象,我们可以用Java的Optional来包装可能为null的结果,这样流中传递的是Optional实例,从根源上避免null:
public void getDataFromTwoSources(DisposableObserver<ComplexUserDto> observer, String shopId) { Preconditions.checkNotNull(observer); Observable<Optional<UserData>> userDataObservable = firstRepository.getUserData(userId) .map(Optional::ofNullable) // 将结果包装为Optional,null会转为Optional.empty() .defaultIfEmpty(Optional.empty()); // 确保流不会因为无数据而中断 Observable<Optional<UserDetailData>> userDetailObservable = secondRepository.getUserDetailData(userId) .map(Optional::ofNullable) .defaultIfEmpty(Optional.empty()); final Observable<ComplexUserDto> observable = Observable.zip(userDataObservable, userDetailObservable, (userDataOpt, userDetailsOpt) -> { // 从Optional中取出值,不存在则用null(需确保ComplexUserDto能接受null属性) ComplexUserDto resultDto = new ComplexUserDto( userDataOpt.orElse(null), userDetailsOpt.orElse(null) ); return resultDto; }) .compose(rxTransformers.applyUnauthorizedHandler()) .compose(rxTransformers.applySchedulers()); addDisposable(observable.subscribeWith(observer)); }
这种方式既能保留“是否有数据”的状态,又不会违反RxJava2的非null规则。
方案3:过滤Null值(谨慎使用)
如果某个数据源返回null时,你可以接受跳过该结果,但要注意:zip操作符需要两个流都发射数据才会执行合并,所以如果其中一个流因为过滤没有输出,整个zip流会一直等待,永远不会返回结果。这个方案仅适合特殊场景:
Observable<UserData> userDataObservable = firstRepository.getUserData(userId) .filter(Objects::nonNull); // 过滤掉null值
额外建议:检查Realm查询的Observable实现
从崩溃日志看,null是从Realm的Observable中发射出来的,你可以在Repository层提前处理null:
// 示例Realm查询的Observable实现 public Observable<UserData> getUserData(String userId) { return Observable.create(emitter -> { Realm realm = Realm.getDefaultInstance(); try { UserData userData = realm.where(UserData.class).equalTo("userId", userId).findFirst(); // 这里直接处理null,避免发射null到流中 emitter.onNext(userData != null ? userData : new UserData()); emitter.onComplete(); } catch (Exception e) { emitter.onError(e); } finally { realm.close(); } }); }
内容的提问来源于stack exchange,提问作者yozhik
相关产品推荐
相关产品推荐

