You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:43:34