一、背景
在 Android 开发中,异步处理几乎贯穿整个应用生命周期,例如:
- 网络请求(IO线程)
- UI更新(主线程)
- 多接口串联(A → B → C)
- 事件流处理(点击、输入、滑动、传感器数据)
如果使用传统回调方式,代码往往会变成回调地狱:
requestA(a -> {
requestB(a, b -> {
requestC(b, c -> {
updateUI(c);
});
});
});
存在以下问题是:
- 逻辑被拆散在多个回调中
- 线程切换无法统一管理
- 错误处理复杂
- 扩展性极差
RxJava要解决的核心问题
用“统一的数据流模型”去描述异步与事件处理
二、RxJava介绍
1. 核心思想:把一切变成数据流
RxJava 的核心抽象是:所有数据和事件都可以看作一条“流”
数据源 → 操作符链 → 消费者
这条链路具有三个特点:
- 数据是“持续流动”的
- 中间可以任意加工
- 最终由消费者统一接收
2. 三个核心角色
(1)Observable(生产者)
负责产生数据:
Observable.just(1, 2, 3)
可以理解为:“数据源头”
(2)Observer(消费者)
负责接收数据:
.subscribe(data -> show(data));
可以理解为:“最终处理者”
(3)操作符(加工链)
用于对数据进行处理,例如:
- map:转换数据
- filter:过滤数据
- flatMap:拆分或扩展异步链路
这些操作符的作用是:在不破坏数据流结构的情况下,对数据进行逐层加工
3. 冷流 vs 热流
冷流(Cold Observable)
Observable.just(1,2,3)
特点:
- subscribe 才开始执行
- 每次订阅都会重新产生数据
- 数据是“从头生成”的
👉 类比: 点外卖:每次下单都是重新做一份
热流(Hot Observable)
例如 Subject:
- 数据持续产生
- 订阅者加入时只能接收“当前之后”的数据
- 类似广播或直播流
👉 类比:你进来时,主播已经在播了
三、RxJava原理
1. RxJava调用原理
1.1 链式结构本质:不是执行,而是包装
RxJava 的链式调用:
Observable.just(1)
.map(x -> x + 1)
.filter(x -> x > 1)
表面是链式调用,但本质是:
每一层都在创建一个新的 Observable,并持有上游
结构变成:
ObservableFilter
↓
ObservableMap
↓
ObservableJust
map操作符本质
new ObservableMap(source, mapper)
这里有两个关键点:
- source:上游数据源
- mapper:数据转换逻辑
👉 每一层只是“包装”,并不执行
1.2 subscribe 才是真正执行入口
RxJava 是典型的懒执行模型:
链式构建(不执行)
↓
subscribe触发
↓
开始真正运行
1.3 订阅过程:从下往上建立链
当调用 subscribe 时:
Observer(最终消费者)
↑
MapObserver
↑
FilterObserver
↑
Source
核心过程:下游 Observer 被一层层包装后传递给上游
1.4 数据流动过程:从上往下执行
当数据产生后:
Source → Filter → Map → Observer
特点:
- 数据逐层向下流动
- 每一层都有机会处理数据
- 可以中途修改、拦截、丢弃
1.5 调用原理本质
👉 RxJava调用本质:
链式包装(构建阶段) + subscribe触发(执行阶段) + 数据逐层流动(运行阶段)
2. 线程切换机制(subscribeOn / observeOn)
2.1 subscribeOn(控制上游执行线程)
作用:决定“数据从哪里开始产生”
实现本质:
把 subscribe 过程包装成 Runnable
提交到线程池执行
特点:
- 只生效一次
- 控制整个上游链路
- 影响数据“生产阶段”
2.2 observeOn(控制下游消费线程)
作用:决定“数据在哪个线程被处理”
实现本质:
1. 上游发数据 onNext
2. 先进入队列缓存
3. 目标线程消费队列
Android实现:
Handler(Looper.getMainLooper()).post(...)
2.3 为什么需要队列?
因为:
- 不能跨线程直接调用 UI
- 需要保证顺序
- 需要解耦生产与消费速度
结构:IO线程 → 队列 → 主线程
2.4 核心区别总结
| 方法 | 作用 |
|---|---|
| subscribeOn | 控制“在哪生产数据” |
| observeOn | 控制“在哪消费数据” |
2.5 线程模型本质
subscribeOn 控制生产线程,observeOn 控制消费线程,本质是“线程池 + 队列”的调度模型
3. 背压处理(Backpressure)
3.1 核心问题:上下游速度不匹配
当出现:
上游:1000条/秒
下游:100条/秒
会发生:
- 数据不断堆积
- 内存不断增长
- 最终 OOM
3.2 背压的核心思想
让下游控制上游的发送速度
request(n) → 限制上游最多发送n个数据
Flowable(背压实现者)
Observable ❌ 不支持背压
Flowable ✅ 支持背压
运行模型
上游 → 缓冲区 → 下游 request
request机制
request(100)
含义:告诉上游:“你最多发100个给我”
3.3 背压策略
BUFFER
- 全部缓存
- 优点:不丢数据
- 缺点:可能 OOM
DROP
- 来不及就丢弃
- 适合非关键数据
LATEST
- 只保留最新数据
- 类似“状态更新”
MISSING
- 不处理背压
- 交由开发者控制
3.4 背压底层机制
核心逻辑:
1. 下游发 request(n)
2. 上游记录可发送数量
3. 每发送一个递减
4. 为0则停止发送
3.5 背压本质总结
背压本质是“通过 request 控制数据流速,从而匹配上下游处理能力”
四、总结
RxJava 通过链式包装 Observable 构建数据流,在 subscribe 时触发执行,通过 Observer 逐层传递数据,并结合 Scheduler 实现线程切换,最终通过 Flowable 的 request 机制实现背压控制,从而构建一套高可组合性的异步数据流处理框架。