Android平台JavaRx中如何从同一个Observable获取两类不同对象列表
最佳实现方案
直接在现有Observable链路的最开头添加doOnNext操作符存储原始列表即可,无需修改原有逻辑,也不会触发重复API请求:
Disposable disposable = myObjectsRepository.getMyObjectsObservable() // 新增这一行即可完成原始列表存储,整个流仅执行一次 .doOnNext(rawMyObjects -> myObjectsList = rawMyObjects) .concatMapIterable(myObject -> myObject) .map(myObject -> myObject.getString1() + " And " + myObject.getString2()) .toList() .retry(4) // 注意:原代码将doOnError放在retry前会导致每次重试都触发错误回调,移到retry后仅重试全部失败时触发一次 .doOnError(throwable -> { succeededLiveData.postValue(false); myObjectStringsListLiveData.postValue(null); }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(myObjectStringsList -> { succeededLiveData.setValue(true); myObjectStringsListLiveData.setValue(myObjectStringsList); }, throwable -> { succeededLiveData.setValue(false); myObjectStringsListLiveData.setValue(null); });
方案优势
- 完全不需要引入Subject或多订阅逻辑,仅加一行代码就满足需求,不会触发重复API请求
- 保留了原有RxJava流式处理的写法,字符串转换逻辑依然在后台线程执行,不会阻塞主线程
- 不存在线程安全问题:ViewModel的普通成员变量没有线程限制,只要没有多线程并发写的场景,在Rx后台线程赋值完全合法,只有LiveData的
setValue要求必须在主线程调用,普通成员变量无此限制。
对你提出的两种方案的评价
- Subject方案:可以实现但完全没必要,会增加额外的订阅管理成本,还需要处理Subject的生命周期,反而容易引入内存泄漏风险
- 订阅回调内做转换方案:可以实现但不够优雅,把流处理逻辑都放到订阅回调里会让代码结构混乱,而且如果转换逻辑复杂确实会占用主线程时间,不如直接用Rx操作符在后台处理更合理。
附加问题解答:postValue的执行顺序
postValue本质是把更新任务提交到主线程的消息队列,主线程Looper会严格按照消息入队顺序执行任务:
- 同一进程内,你按顺序调用两个不同LiveData的
postValue,两个任务会按调用顺序进入主线程消息队列,也会按顺序执行通知,不会出现后调用的postValue先触发观察者回调的情况。 - 如果你担心出现顺序问题,更稳妥的做法是将两个状态封装成一个独立的UI状态类,只用一个LiveData存储,从根源上避免状态不同步的问题:
// 定义UI状态类 public class MyObjectsUiState { public final boolean isSuccess; public final List<String> displayStrings; public MyObjectsUiState(boolean isSuccess, List<String> displayStrings) { this.isSuccess = isSuccess; this.displayStrings = displayStrings; } } // 仅用一个LiveData存储状态,完全避免顺序问题 private MutableLiveData<MyObjectsUiState> uiStateLiveData = new MutableLiveData<>();
内容的提问来源于stack exchange,提问作者programmingmanwithaplan
相关产品推荐
相关产品推荐

