0x04 RxJava介绍

9 阅读5分钟

一、背景

在 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 机制实现背压控制,从而构建一套高可组合性的异步数据流处理框架。