第一次接触RxJava时,我被它"异步数据流处理"的概念深深吸引。传统Android开发中,我们习惯用回调处理异步任务,但随着业务复杂度提升,回调嵌套(俗称"回调地狱")让代码变得难以维护。RxJava通过观察者模式将数据产生和消费解耦,配合丰富的操作符,让异步代码像流水线一样清晰。
举个实际场景:我们需要从网络获取用户数据,过滤掉无效项,然后更新UI。传统实现需要嵌套3层回调,而用RxJava可以写成链式调用:
java复制api.getUsers()
.filter(user -> user.isValid())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(users -> updateUI(users));
这种声明式编程让业务逻辑一目了然。更重要的是,RxJava内置了线程调度、错误处理等机制,避免了手动管理线程的繁琐和风险。
Observable是被观察的数据源,Observer是数据的消费者。它们的关系就像杂志出版社和订阅者:
java复制Observable.create(emitter -> {
emitter.onNext("Issue 1"); // 发送数据
emitter.onComplete(); // 标记完成
}).subscribe(
issue -> System.out.println("收到: " + issue), // onNext
error -> error.printStackTrace(), // onError
() -> System.out.println("杂志已停刊") // onComplete
);
关键点:
onNext() 可以调用多次,每次传递一个数据项onError() 和 onComplete() 互斥,只能调用其中一个RxJava的强大之处在于300+操作符,以下是新手必备的5类:
创建型
just(): 直接发射指定数据
fromIterable(): 遍历集合发射数据
interval(): 定时发射序列
转换型
map(): 数据一对一转换
flatMap(): 异步转换并合并结果
java复制Observable.just("user1", "user2")
.flatMap(id -> api.getUserDetail(id)) // 每个ID转为新的Observable
.subscribe(user -> showUser(user));
过滤型
filter(): 条件过滤
take(): 只取前N项
debounce(): 防抖动(搜索框场景)
组合型
merge(): 合并多个Observable
zip(): 按顺序组合数据
java复制Observable.zip(
api.getUserProfile(),
api.getUserOrders(),
(profile, orders) -> new UserData(profile, orders)
).subscribe();
错误处理
onErrorReturn(): 出错时返回默认值
retryWhen(): 条件重试
RxJava通过Schedulers指定操作执行的线程:
Schedulers.io(): 网络/文件等IO操作Schedulers.computation(): CPU密集型计算AndroidSchedulers.mainThread(): Android主线程典型用法:
java复制Observable.create(emitter -> {
// 在IO线程执行耗时操作
String data = downloadFromNetwork();
emitter.onNext(data);
})
.subscribeOn(Schedulers.io()) // 指定上游执行线程
.observeOn(AndroidSchedulers.mainThread()) // 指定下游回调线程
.subscribe(data -> updateUI(data));
重要规则:
subscribeOn()只有第一次调用有效,observeOn()可以多次调用改变后续操作线程
java复制// ViewModel层
public Observable<WeatherData> fetchWeather(String city) {
return api.getCurrentWeather(city)
.zipWith(api.getForecast(city),
(current, forecast) -> combineData(current, forecast))
.retryWhen(errors -> errors.flatMap(error -> {
if (error instanceof IOException) {
return Observable.timer(3, TimeUnit.SECONDS);
}
return Observable.error(error);
}))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
// Activity层
disposable = viewModel.fetchWeather(city)
.debounce(500, TimeUnit.MILLISECONDS) // 防抖500ms
.subscribe(
data -> {
binding.progressBar.setVisibility(View.GONE);
updateWeatherUI(data);
},
error -> {
binding.progressBar.setVisibility(View.GONE);
showErrorToast(error.getMessage());
}
);
内存泄漏防护
在onDestroy()中调用disposable.dispose()
java复制private CompositeDisposable disposables = new CompositeDisposable();
disposables.add(observable.subscribe());
@Override
protected void onDestroy() {
disposables.clear();
super.onDestroy();
}
背压策略
当生产者速度 > 消费者速度时,需要背压控制:
java复制Observable.range(1, 1000000)
.onBackpressureBuffer(1000) // 缓冲区1000项
.subscribe();
调试技巧
添加doOnNext()打印日志:
java复制observable
.doOnNext(data -> Log.d("RxJava", "收到数据: " + data))
.subscribe();
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 没有收到数据 | 忘记调用subscribe() | 检查是否添加订阅 |
| 回调不在主线程 | 漏掉observeOn() | 添加.observeOn(AndroidSchedulers.mainThread()) |
| 重复订阅 | 每次点击都新建Observable | 使用share()或replay()共享数据源 |
| 内存泄漏 | 未及时dispose | 使用CompositeDisposable统一管理 |
| 数据顺序错乱 | 使用flatMap并发处理 | 改用concatMap保持顺序 |
避免过度订阅
对于静态数据使用cache():
java复制Observable<User> userObservable = api.getUser()
.subscribeOn(Schedulers.io())
.cache();
合理使用操作符
flatMap vs concatMap: 前者并发执行,后者顺序执行throttleFirst vs debounce: 前者取周期内第一个事件,后者等停顿后取最后事件冷热Observable转换
将冷Observable转为热Observable减少重复计算:
java复制ConnectableObservable<Data> hot = observable.publish();
hot.connect(); // 开始发射数据
hot.subscribe(); // 多个订阅者共享数据
RxJava提供TestObserver和TestScheduler方便测试:
java复制@Test
public void testFetchWeather() {
// 1. 创建测试Observer
TestObserver<WeatherData> testObserver = new TestObserver<>();
// 2. 执行被测试代码
viewModel.fetchWeather("Beijing").subscribe(testObserver);
// 3. 验证结果
testObserver.assertNoErrors();
testObserver.assertValueCount(1);
testObserver.assertValue(data -> data.getCity().equals("Beijing"));
}
对于时间相关操作,使用TestScheduler虚拟时间:
java复制TestScheduler scheduler = new TestScheduler();
Observable.interval(1, TimeUnit.SECONDS, scheduler)
.take(5)
.subscribe(System.out::println);
scheduler.advanceTimeBy(5, TimeUnit.SECONDS); // 立即触发5次发射
官方文档
ReactiveX官方文档 有各语言通用概念说明
可视化工具
RxMarbles 交互式演示操作符效果
进阶书籍
《RxJava反应式编程》详细讲解背压、自定义操作符等高级特性
实战项目
GitHub搜索"rxjava android example"参考成熟实现
从RxJava 2升级到3的主要变化:
io.reactivex改为io.reactivex.rxjava3Flowable成为背压处理的默认选择onErrorComplete等操作符null值支持(更严格的空安全)迁移步骤:
subscribe()现在返回Disposable)在MVVM模式中,RxJava常作为ViewModel和Repository层的连接桥梁:
kotlin复制// Repository层
fun getData(): Observable<Data> {
return localDataSource.getData()
.onErrorResumeNext { remoteDataSource.getData() }
}
// ViewModel层
val uiState = repository.getData()
.map { DataState.Success(it) }
.onErrorReturn { DataState.Error(it.message) }
.startWith(DataState.Loading)
这种模式实现了:
使用RxJavaPlugins全局钩子捕获问题:
java复制RxJavaPlugins.setErrorHandler(e -> {
if (e instanceof UndeliverableException) {
// 处理未被消费的异常
logError(e.getCause());
}
});
结合Android Profiler监控:
我在实际项目中发现,过度使用observeOn()会导致频繁线程切换,适当合并相邻操作可以提升性能。例如网络请求后的数据转换可以在IO线程完成,最后再切回主线程更新UI。