在 Android 开发中,异步任务处理是绕不开的核心场景 —— 网络请求、数据库操作、文件读写等都需要在后台执行,而结果需回调到主线程更新 UI。传统的 “Handler+Thread” 或 AsyncTask 不仅代码冗余,还容易陷入 “回调地狱”(嵌套回调导致代码可读性差)。RxJava 作为一款基于响应式编程思想的异步框架,通过 “链式调用” 和 “操作符” 完美解决了这些问题,成为 Android 开发者的必备工具。本文将从 RxJava 的核心原理、核心组件到 Android 实战,全面讲解 RxJava 的使用。
响应式编程(Reactive Programming)是一种 “以数据流和变化传播为核心” 的编程范式。简单来说,就是将所有操作抽象为 “数据流”,数据的产生、转换、消费都通过数据流传递,当数据变化时,依赖该数据的操作会自动响应。
RxJava(Reactive Extensions for Java)是响应式编程在 Java 平台的实现,其核心思想是:
形象比喻:RxJava 就像 “水管系统”—— 被观察者是 “水源”,操作符是 “水管中的过滤器 / 转换器”,观察者是 “水龙头”。水流(数据)从水源出发,经过过滤、转换后,最终从水龙头流出被使用。
对比传统异步方式,RxJava 的优势体现在三个方面:
1.消除回调地狱
传统嵌套回调(如 “网络请求 1→网络请求 2→更新 UI”)的代码如下:
// 传统回调嵌套(回调地狱)
api.requestData1(new Callback() {
@Override
public void onSuccess(Data1 data1) {
api.requestData2(data1.getId(), new Callback() {
@Override
public void onSuccess(Data2 data2) {
runOnUiThread(() -> updateUI(data2));
}
@Override
public void onFailure(Throwable e) { ... }
});
}
@Override
public void onFailure(Throwable e) { ... }
});
用 RxJava 实现的链式调用:
// RxJava链式调用(无嵌套)
api.rxRequestData1() // 第一步:请求数据1
.flatMap(data1 -> api.rxRequestData2(data1.getId())) // 第二步:用数据1请求数据2
.observeOn(AndroidSchedulers.mainThread()) // 切换到主线程
.subscribe(
data2 -> updateUI(data2), // 成功回调
e -> handleError(e) // 错误回调
);
2.线程切换简化
传统方式需通过 Handler 手动切换线程,RxJava 通过subscribeOn和observeOn两个操作符即可指定 “任务执行线程” 和 “回调线程”,无需手动处理线程切换。
3.数据处理标准化
无论是网络请求、数据库查询还是事件监听,都可抽象为 Observable,通过统一的操作符进行处理(如过滤空数据、转换数据格式),降低代码耦合。
RxJava 的核心组件包括 “被观察者(Observable)”“观察者(Observer)”“订阅(Subscribe)”“操作符(Operator)”,这四个组件构成了 RxJava 的基本骨架。
Observable 是数据的 “源头”,负责产生数据(可以是单个数据、多个数据或一个错误)。其生命周期包含三个关键事件:
简单示例:创建一个发送 3 个整数的 Observable
// 创建被观察者:发送1、2、3三个数据,然后完成
Observable observable = Observable.create(emitter -> {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
emitter.onComplete(); // 数据发送完成
});
除了create,RxJava 还提供了便捷的创建方法:
示例:用just创建 Observable
Observable observable = Observable.just("A", "B", "C"); // 发送A、B、C
Observer 是数据的 “消费者”,负责接收 Observable 发送的事件(onNext/onError/onComplete)并处理。RxJava 中有两种常用的观察者接口:
Observer observer = new Observer() {
@Override
public void onSubscribe(Disposable d) {
// 订阅时调用(可保存Disposable用于取消订阅)
mDisposable = d;
}
@Override
public void onNext(String s) {
// 接收数据(对应Observable的onNext)
Log.d("RxJava", "收到数据:" + s);
}
@Override
public void onError(Throwable e) {
// 接收错误(对应Observable的onError)
Log.e("RxJava", "发生错误:" + e.getMessage());
}
@Override
public void onComplete() {
// 接收完成通知(对应Observable的onComplete)
Log.d("RxJava", "数据接收完成");
}
};
// 只处理正常数据
Consumer onNext = s -> Log.d("RxJava", "收到数据:" + s);
// 处理错误
Consumer onError = e -> Log.e("RxJava", "错误:" + e.getMessage());
Observable 和 Observer 本身是独立的,需通过 “订阅(subscribe)” 建立关联。调用observable.subscribe(observer)后,Observable 开始发送数据,Observer 开始接收数据。
订阅示例:
// Observable与Observer建立订阅关系
Disposable disposable = observable.subscribe(
s -> Log.d("RxJava", "收到:" + s), // onNext
e -> Log.e("RxJava", "错误:" + e.getMessage()), // onError
() -> Log.d("RxJava", "完成"), // onComplete
d -> mDisposable = d // onSubscribe(可选)
);
关键对象:Disposable
subscribe方法返回的Disposable(可理解为 “开关”)用于取消订阅:
为什么需要取消订阅?
若页面销毁后,Observable 仍在发送数据并回调 UI,会导致内存泄漏(Observer 持有 Activity 引用)。需在onDestroy中调用dispose():
@Override
protected void onDestroy() {
super.onDestroy();
if (mDisposable != null && !mDisposable.isDisposed()) {
mDisposable.dispose(); // 取消订阅,避免内存泄漏
}
}
操作符是 RxJava 的 “灵魂”,用于在数据从 Observable 到 Observer 的过程中进行转换、过滤、组合等操作。RxJava 提供了上百种操作符,按功能可分为几类核心操作符。
// 将Integer转换为String(1→"Number:1")
Observable.just(1, 2, 3)
.map(number -> "Number: " + number) // 转换逻辑
.subscribe(s -> Log.d("RxJava", s)); // 输出:Number: 1、Number: 2、Number: 3
// 模拟:根据用户ID获取用户信息,再根据用户信息获取订单列表
Observable.just(1001) // 用户ID
.flatMap(userId -> getUserInfo(userId)) // 转换为“用户信息Observable”
.flatMap(userInfo -> getOrderList(userInfo.getUserId())) // 转换为“订单列表Observable”
.subscribe(orders -> updateOrderUI(orders));
// 筛选偶数
Observable.just(1, 2, 3, 4, 5)
.filter(number -> number % 2 == 0) // 条件:偶数
.subscribe(number -> Log.d("RxJava", "偶数:" + number)); // 输出:2、4
// 只取前2个数据
Observable.just("A", "B", "C", "D")
.take(2)
.subscribe(s -> Log.d("RxJava", s)); // 输出:A、B
Android 开发中最常用的操作符,用于指定 “任务执行线程” 和 “回调线程”:
RxJava 通过Schedulers提供常用线程:
示例:网络请求在 IO 线程执行,结果在主线程回调
// 需添加RxAndroid依赖(提供mainThread())
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
// 线程切换示例
Observable.fromCallable(() -> {
// 耗时操作(在IO线程执行)
return fetchDataFromNetwork(); // 网络请求
})
.subscribeOn(Schedulers.io()) // 指定发送数据的线程(IO线程)
.observeOn(AndroidSchedulers.mainThread()) // 指定接收数据的线程(主线程)
.subscribe(
data -> updateUI(data), // 在主线程更新UI
e -> showError(e)
);
Observable observable1 = Observable.just("A", "B");
Observable observable2 = Observable.just("C", "D");
Observable.concat(observable1, observable2)
.subscribe(s -> Log.d("RxJava", s)); // 输出:A、B、C、D
Observable observable1 = Observable.just(1, 2);
Observable observable2 = Observable.just("A", "B");
Observable.zip(
observable1,
observable2,
(num, str) -> num + str // 合并逻辑:1+"A"→"1A",2+"B"→"2B"
).subscribe(s -> Log.d("RxJava", s)); // 输出:1A、2B
RxJava 在 Android 开发中的核心价值是 “简化异步任务 + 线程切换”,以下是几个高频场景及实现。
场景:调用接口获取数据,在主线程更新 UI(避免手动线程切换)。
示例(结合 Retrofit,Retrofit 原生支持返回 Observable):
// 1. 定义Retrofit接口(返回Observable)
public interface ApiService {
@GET("user/{id}")
Observable getUserInfo(@Path("id") String userId);
}
// 2. 创建Retrofit实例
ApiService api = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create()) // 支持RxJava
.build()
.create(ApiService.class);
// 3. 发起请求并处理结果
Disposable disposable = api.getUserInfo("1001")
.subscribeOn(Schedulers.io()) // 网络请求在IO线程
.observeOn(AndroidSchedulers.mainThread()) // 回调在主线程
.subscribe(
user -> {
// 更新UI
tvName.setText(user.getName());
tvAge.setText(String.valueOf(user.getAge()));
},
e -> {
// 处理错误(如网络异常)
Toast.makeText(this, "请求失败", Toast.LENGTH_SHORT).show();
}
);
// 4. 页面销毁时取消订阅
@Override
protected void onDestroy() {
super.onDestroy();
if (disposable != null && !disposable.isDisposed()) {
disposable.dispose();
}
}
场景:从数据库查询数据,过滤无效数据后显示(用操作符简化数据处理)。
示例(结合 Room,Room 支持返回 Observable):
// 1. Room实体类
@Entity
public class User {
@PrimaryKey
public String id;
public String name;
public int age;
}
// 2. Room DAO(返回Observable)
@Dao
public interface UserDao {
@Query("SELECT * FROM user")
Observable> getAllUsers();
}
// 3. 查询并过滤数据(只显示成年人)
Disposable disposable = userDao.getAllUsers()
.subscribeOn(Schedulers.io()) // 数据库操作在IO线程
.map(users -> {
// 过滤年龄≥18的用户(map转换)
List adults = new ArrayList<>();
for (User user : users) {
if (user.age >= 18) {
adults.add(user);
}
}
return adults;
})
.observeOn(AndroidSchedulers.mainThread()) // 主线程更新列表
.subscribe(
adults -> userAdapter.setData(adults),
e -> Log.e("DB", "查询失败:" + e.getMessage())
);
场景:实现倒计时(如验证码倒计时 60 秒)。
示例:
// 倒计时60秒(从60到0)
Disposable disposable = Observable.interval(0, 1, TimeUnit.SECONDS) // 立即执行,每秒一次
.take(61) // 只取61个数据(0-60)
.map(count -> 60 - count) // 转换为倒计时(60,59,...,0)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
second -> {
// 更新按钮文字
btnCode.setText(second + "秒后重新发送");
btnCode.setEnabled(second == 0); // 倒计时结束后可点击
},
e -> Log.e("Countdown", "错误:" + e.getMessage())
);
场景:需同时调用两个接口,合并结果后显示(如获取用户信息 + 用户订单)。
示例(用 zip 合并两个请求):
// 1. 两个接口请求
Observable userObservable = api.getUserInfo("1001");
Observable> ordersObservable = api.getOrderList("1001");
// 2. 合并结果
Disposable disposable = Observable.zip(
userObservable,
ordersObservable,
(user, orders) -> new UserWithOrders(user, orders) // 合并为新对象
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
userWithOrders -> {
// 显示用户信息和订单
showUserInfo(userWithOrders.user);
showOrders(userWithOrders.orders);
},
e -> Toast.makeText(this, "请求失败", Toast.LENGTH_SHORT).show()
);
内存泄漏是 RxJava 最常见的问题,根本原因是 “Observer 持有 Activity/Fragment 引用,且未取消订阅”。解决方法:
当有多个订阅时,用CompositeDisposable统一管理:
private CompositeDisposable mCompositeDisposable = new CompositeDisposable();
// 添加订阅
mCompositeDisposable.add(disposable1);
mCompositeDisposable.add(disposable2);
// 取消所有订阅(在onDestroy中)
@Override
protected void onDestroy() {
super.onDestroy();
mCompositeDisposable.dispose(); // 一次性取消所有订阅
}
在 Observer 中若需引用 Activity,用弱引用避免强持有:
Observer observer = new Observer() {
private WeakReference activityRef;
public Observer(MainActivity activity) {
activityRef = new WeakReference<>(activity);
}
@Override
public void onNext(User user) {
MainActivity activity = activityRef.get();
if (activity != null && !activity.isFinishing()) {
activity.updateUI(user); // 仅当Activity有效时更新
}
}
// ...其他方法
};
RxJava 中若未处理onError,会导致程序崩溃。可通过onErrorResumeNext或全局异常处理器统一处理:
// 局部错误处理(返回默认数据)
Observable.just("1001")
.flatMap(id -> api.getUserInfo(id)
.onErrorResumeNext(throwable -> {
// 发生错误时返回默认用户
return Observable.just(new User("默认用户", 0));
})
)
.subscribe(...);
// 全局错误处理(通过Transformer)
public ObservableTransformer handleError() {
return upstream -> upstream
.onErrorResumeNext(throwable -> {
Log.e("GlobalError", "错误:" + throwable.getMessage());
return Observable.empty(); // 发生错误时发送空数据
});
}
// 使用全局处理器
api.getUserInfo("1001")
.compose(handleError()) // 应用全局错误处理
.subscribe(...);
虽然链式调用简洁,但过度使用操作符会导致:
建议:
目前 RxJava 已发展到 RxJava3,相比 RxJava2 的主要变化:
建议直接使用 RxJava3,避免兼容旧版本问题。
框架 |
优势 |
劣势 |
适用场景 |
RxJava |
操作符丰富,灵活度高,支持复杂数据处理 |
学习成本高,依赖较多 |
复杂异步场景(多请求合并、数据转换) |
Kotlin 协程 |
语言级支持,轻量级,无额外依赖 |
缺乏操作符,复杂转换需手动实现 |
简单异步任务,Kotlin 项目 |
LiveData |
生命周期感知,自动取消订阅 |
操作符少,不支持复杂转换 |
数据与 UI 绑定(配合 ViewModel) |
最佳实践:
RxJava 的核心价值在于将异步任务 “数据流化”,通过观察者模式和操作符简化数据的产生、转换、消费流程。其在 Android 开发中的核心应用是:
学习 RxJava 的关键是 “理解观察者模式” 和 “掌握核心操作符”,而非死记硬背所有操作符。实际开发中,应根据场景选择合适的操作符,避免过度设计。同时,务必注意 “取消订阅” 以防止内存泄漏 —— 这是 RxJava 使用的第一准则。
掌握 RxJava 后,你会发现异步任务处理从 “繁琐的回调嵌套” 变成 “流畅的链式调用”,代码可读性和可维护性将大幅提升。