深入浅出安卓RxJava原理
一、RxJava是什么?——快递公司模型
想象RxJava是一个智能快递公司系统:
- **你(开发者)**是发货客户
- 数据是要运送的包裹
- **Observable(被观察者)**是快递订单
- **Observer(观察者)**是收件人
- 操作符是物流中转站(可以对包裹进行加工)
- **Scheduler(调度器)**是物流路线规划员
二、核心工作原理:观察者模式升级版
1. 基本流程
// 1. 创建快递订单(被观察者)
Observable<String> order = Observable.create(emitter -> {
emitter.onNext("包裹1"); // 发出包裹
emitter.onNext("包裹2");
emitter.onComplete(); // 配送完成
});
// 2. 客户签收(观察者)
Observer<String> customer = new Observer<String>() {
@Override
public void onNext(String s) {
System.out.println("收到:" + s); // 处理包裹
}
@Override
public void onComplete() {
System.out.println("所有包裹已送达");
}
};
// 3. 建立订阅关系(下单)
order.subscribe(customer);
2. 关键角色解析
| 组件 | 作用 | 生活类比 |
|---|---|---|
| Observable | 数据生产者 | 快递发货方 |
| Observer | 数据消费者 | 收件人 |
| subscribe | 建立连接 | 下单 |
| onNext | 发送数据 | 派送包裹 |
| onComplete | 完成通知 | 配送完成 |
| onError | 错误处理 | 包裹丢失通知 |
三、线程控制原理——物流调度中心
RxJava最强大的特性之一:轻松切换线程
1. 调度器类型
Observable.create(...)
.subscribeOn(Schedulers.io()) // 发货仓库(IO线程)
.observeOn(AndroidSchedulers.mainThread()) // 送货上门(主线程)
.subscribe(...);
| 调度器 | 用途 | 对应线程 |
|---|---|---|
| Schedulers.io() | 网络/文件操作 | IO线程池 |
| Schedulers.computation() | 计算任务 | CPU核心数线程池 |
| AndroidSchedulers.mainThread() | UI更新 | 主线程 |
| Schedulers.newThread() | 普通后台任务 | 新建线程 |
2. 线程切换原理
RxJava内部通过Handler+ExecutorService实现:
subscribeOn:指定上游执行的线程池observeOn:改变下游执行的线程
就像:
- 快递从上海仓库发出(io线程)
- 在北京中转站换车(observeOn切换)
- 最后用电动车派送(主线程)
四、操作符原理——物流加工流水线
1. 常见操作符示例
Observable.fromIterable(list) // 源头发货
.filter(item -> item.price > 100) // 质检过滤
.map(item -> item.name) // 重新包装
.take(5) // 限量发货
.subscribe(...); // 最终收货
2. 操作符实现原理
每个操作符都会创建一个新的Observable,形成链式调用:
// 简化版map操作符实现
public final <R> Observable<R> map(Function<T, R> mapper) {
return new Observable<R>() {
@Override
protected void subscribeActual(Observer<? super R> observer) {
// 创建中间观察者
source.subscribe(new Observer<T>() {
@Override
public void onNext(T t) {
// 关键转换代码
observer.onNext(mapper.apply(t));
}
});
}
};
}
就像:每个中转站都会拆开包裹,按规则重新包装后再发出
五、背压(Backpressure)——物流爆仓问题
当发货速度 >> 收货速度时,会出现内存溢出(OOM)
1. 解决方案
// 使用Flowable处理背压
Flowable.range(1, 1000000)
.onBackpressureBuffer() // 缓冲策略
.subscribe(...);
2. 背压策略
| 策略 | 行为 | 类比 |
|---|---|---|
| BUFFER | 缓存所有数据 | 租临时仓库 |
| DROP | 丢弃超量数据 | 拒收新包裹 |
| LATEST | 保留最新数据 | 只留最新包裹 |
六、Disposable——快递订单取消
// 订阅时获得Disposable
Disposable disposable = observable.subscribe(...);
// 需要时取消订阅(避免内存泄漏)
disposable.dispose();
原理:内部通过一个原子布尔标志位isDisposed控制
七、RxJava与LiveData对比
| 特性 | RxJava | LiveData |
|---|---|---|
| 线程切换 | 灵活支持所有线程 | 自动切主线程 |
| 生命周期感知 | 需要手动管理 | 自动感知 |
| 操作符 | 丰富强大 | 简单有限 |
| 学习曲线 | 陡峭 | 平缓 |
| 适用场景 | 复杂数据流 | UI数据观察 |
八、最佳实践
- 内存泄漏防护:
// 在Activity中使用
private CompositeDisposable bag = new CompositeDisposable();
void someMethod() {
bag.add(observable.subscribe(...));
}
@Override
protected void onDestroy() {
bag.clear(); // 统一清理
super.onDestroy();
}
- 错误处理:
.subscribe(
item -> {...}, // onNext
throwable -> { // onError
Log.e("RxJava", "出错啦", throwable);
}
);
- 调试技巧:
Observable.range(1, 5)
.doOnNext(i -> Log.d("TAG", "发射:" + i))
.subscribe(...);
九、原理总结
RxJava的核心设计思想:
- 观察者模式:建立生产者-消费者关系
- 装饰器模式:操作符层层包装
- 响应式编程:数据流驱动
- 函数式编程:无副作用、链式调用
工作流程比喻:
- 创建订单(Observable.create)
- 添加物流方案(操作符+调度器)
- 客户下单(subscribe)
- 开始发货(onNext)
- 异常处理/完成通知(onError/onComplete)
记住三个关键:
- Observable是冷的:不订阅不执行(像未下单不发货)
- 操作符会创建新Observable:每个中转站都是独立环节
- 线程切换要明确:物流路线规划好才不会送错地方
掌握了RxJava原理,你就能优雅地处理各种异步事件流,让代码像物流系统一样高效有序!