5个RxJava案例教你优雅处理异步任务
目录导读
- RxJava为何仍是异步编程利器?
- 网络请求自动重试(RetryWhen)
- 搜索框防抖(Debounce)
- 并发请求合并(Zip与CombineLatest)
- 缓存优先策略(Concat)
- 错误处理与降级(OnErrorReturn)
- 常见问题问答(FAQ)
- 总结与最佳实践
RxJava为何仍是异步编程利器?
尽管Kotlin协程崛起,但RxJava在复杂数据流变换、背压处理、操作符丰富度上依然不可替代,对于Android开发者而言,理解RxJava的核心思想(观察者模式+链式调用)能极大提升代码可读性,下面通过5个真实案例展示其威力。

案例一:网络请求自动重试(RetryWhen)
场景:用户点击“加载”按钮,网络偶发超时,希望自动重试2次,且每次间隔1秒。
Observable<Response> apiCall = api.getData()
.retryWhen(errors -> errors.zipWith(Observable.range(1, 3), (e, i) -> i)
.flatMap(retryCount -> Observable.timer(1, TimeUnit.SECONDS)))
.doOnSubscribe(d -> showLoading())
.subscribe(response -> showData(), throwable -> showError());
解析:zipWith计数,timer延迟,若3次仍失败则走onError,此案例避免多层嵌套回调,且重试逻辑即时可见。
案例二:搜索框防抖(Debounce)
场景:用户输入关键词实时搜索,避免每次按键都请求服务器。
EditText input = findViewById(R.id.search_input);
RxTextView.textChanges(input)
.debounce(400, TimeUnit.MILLISECONDS)
.filter(text -> text.length() > 2)
.switchMap(query -> api.search(query))
.subscribe(results -> updateList(results));
关键点:
debounce:停止输入400ms后才发射。switchMap:若新输入到来,取消前一个搜索请求,确保只展示最新结果。
效果:极大减少无效请求,提升服务器负载能力。
案例三:并发请求合并(Zip与CombineLatest)
场景:首页需要同时获取用户信息+推荐列表,两者都成功后才展示UI。
Observable<User> userObs = api.getUser();
Observable<List<Item>> itemsObs = api.getRecommendations();
Observable.zip(userObs, itemsObs, (user, items) -> new HomeData(user, items))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> render(data));
对比:CombineLatest用于其中任一数据变化则重新组合(如筛选条件+列表数据)。zip严格等待所有数据就绪。
注意:若请求之间存在依赖(B依赖A的结果),应使用flatMap链式调用。
案例四:缓存优先策略(Concat)
场景:优先展示本地缓存,再请求网络更新。
Observable<Data> cache = db.getCache().onErrorReturn(throwable -> null);
Observable<Data> network = api.getFreshData().doOnNext(data -> db.save(data));
Observable.concat(cache, network)
.firstElement() // 只取第一个有效值
.subscribe(data -> render(data));
优化:若缓存为空,concat会继续订阅network,配合firstElement避免页面闪烁。
案例五:错误处理与降级(OnErrorReturn)
场景:视频详情页加载失败时,显示默认封面图并提示重试。
api.getVideoDetail(id)
.map(video -> video.getPlayUrl())
.onErrorReturn(throwable -> DEFAULT_PLAY_URL)
.subscribe(url -> player.play(url), throwable -> showError());
进阶:onErrorResumeNext可返回新的Observable(如从备用服务器获取)。retry(2)可限制重试次数。
常见问题问答(FAQ)
Q1:RxJava与协程如何选择?
A:团队已是协程风格则选协程;但若遇到复杂事件流(如用户操作+网络状态组合),RxJava操作符更直接,混合使用也很常见(如协程内部用asObservable())。
Q2:如何避免RxJava内存泄漏?
A:使用CompositeDisposable在onDestroy中dispose();或者使用AutoDispose库绑定生命周期。
Q3:背压(Backpressure)是什么?何时需要处理?
A:上游发射速度 > 下游处理速度,当数据量过大(如传感器流)时,使用onBackpressureBuffer或onBackpressureDrop,网络请求一般不会遇到,但数据库大量导出时需注意。
Q4:和LiveData的区别?
A:LiveData负责感知生命周期但无变换能力;RxJava可做复杂变换但需手动管理生命周期,常用搭配:RxJava计算,LiveData观察结果。
Q5:调试有什么技巧?
A:用doOnNext/doOnError打印日志;或使用RxJava 3.x的RxJavaPlugins.setErrorHandler全局捕获异常。
总结与实用建议
- 避免过度设计:简单功能用AsyncTask或回调即可,不要强行上RxJava。
- 命名规范:操作符链按“每一步做一件事”拆分,用换行缩进增加可读性。
- 线程调度:默认在哪个线程订阅就在哪个线程执行
subscribe;联网用subscribeOn(Schedulers.io()),UI更新用observeOn(AndroidSchedulers.mainThread())。 - 测试辅助:用
TestScheduler虚拟时间控制发射,便于单元测试。
5个案例覆盖了重试、防抖、合并、缓存、降级五种高频业务需求,如果你能透彻理解并灵活组合,处理日常App开发中的异步问题将游刃有余,希望本文的代码片段能成为你手边随时翻阅的参考手册。
如果你觉得有用,欢迎分享给团队技术同伴,或者在实际项目中替换为Kotlin版等价写法(如Flow),思路完全一致。