RxJava——Subscriber

Subscriber

一、概述

在 RxJava 2 中,Subscriber​ 是用于订阅 Flowable​ 数据流的核心消费者接口。它是实现背压(Backpressure)​ 机制的关键,与 Subscription对象协同工作,实现下游对上游数据的主动拉取控制。

java 复制代码
public interface Subscriber<T> {
    // 关键方法
    void onSubscribe(@NonNull Subscription s);
    
    void onNext(@NonNull T t);
    void onError(@NonNull Throwable t);
    void onComplete();
}

Subscriber具备了Observer的所有能力,并增加了关键的 onSubscribe(Subscription s)方法。

二、核心方法

2.1、void onSubscribe(@NonNull Subscription s)

这是 Subscriber独有的、最重要的方法,也是与 Observer的关键区别。

  • 调用时机:在订阅建立后,任何 onNext、onError或 onComplete被调用之前,第一个被调用的方法。
  • 参数 Subscription s:这是上游(Flowable)传递给下游(Subscriber)的控制句柄。
  • 必须执行的操作:在此方法中,你必须通过 s.request(long n)来请求数据。如果你不调用 request(),将永远不会收到任何数据!

典型用法:

java 复制代码
@Override
public void onSubscribe(@NonNull Subscription s) {
    this.subscription = s; // 保存起来供后续使用
    // 必须请求数据!
    s.request(1); // 请求第一个数据
    // 或者
    s.request(Long.MAX_VALUE); // 请求所有数据(关闭背压)
}

2.2、void onNext(@NonNull T t)

  • 作用:处理上游发出的数据项。
  • 触发条件:仅当通过 Subscription.request(n)请求了数据,且上游有数据可发时。
  • 重要约束:在 onNext方法内部,你可以再次调用 subscription.request(n) 来请求更多数据,实现拉取式消费模式。

2.3、void onError(@NonNull Throwable t)

  • 作用:处理流中的错误。调用后,流终止,不会再调用 onNext或 onComplete。
  • 触发条件:上游发生错误,或下游在 onNext中抛出异常。

2.4、void onComplete()

  • 作用:处理流正常完成。调用后,流终止。
  • 触发条件:上游已发射完所有数据。

三、与 Subscription 的交互模式

3.1、一次性请求所有数据(关闭背压)

java 复制代码
Flowable.range(1, 1000)
    .subscribe(new Subscriber<Integer>() {
        private Subscription subscription;
        
        @Override
        public void onSubscribe(Subscription s) {
            this.subscription = s;
            s.request(Long.MAX_VALUE); // 一次性请求所有,关闭背压
        }
        
        @Override
        public void onNext(Integer integer) {
            // 处理数据,无需再请求
            System.out.println(integer);
        }
        
        @Override public void onError(Throwable t) { }
        @Override public void onComplete() { }
    });

3.2、拉取式消费(精确控制)

java 复制代码
Flowable.range(1, 1000)
    .subscribe(new Subscriber<Integer>() {
        private Subscription subscription;
        private int processed = 0;
        
        @Override
        public void onSubscribe(Subscription s) {
            this.subscription = s;
            s.request(10); // 初始请求10个
        }
        
        @Override
        public void onNext(Integer integer) {
            // 处理数据
            process(integer);
            processed++;
            
            // 每处理5个数据,再请求5个
            if (processed % 5 == 0) {
                subscription.request(5);
            }
            
            // 如果处理了足够多,可以取消
            if (processed >= 100) {
                subscription.cancel();
            }
        }
        
        @Override public void onError(Throwable t) { }
        @Override public void onComplete() { }
    });

3.3、异步处理与请求

java 复制代码
Flowable.interval(10, TimeUnit.MILLISECONDS)
    .subscribe(new Subscriber<Long>() {
        private Subscription subscription;
        private final ExecutorService executor = Executors.newSingleThreadExecutor();
        
        @Override
        public void onSubscribe(Subscription s) {
            this.subscription = s;
            s.request(1); // 只请求一个,处理完再请求下一个
        }
        
        @Override
        public void onNext(Long item) {
            // 异步处理,不阻塞 onNext 调用
            executor.submit(() -> {
                processItem(item); // 耗时处理
                // 处理完成后,在异步线程中请求下一个
                subscription.request(1);
            });
        }
        
        @Override public void onError(Throwable t) { executor.shutdown(); }
        @Override public void onComplete() { executor.shutdown(); }
    });

四、简化形式

4.1、使用 Lambda 的简化订阅

java 复制代码
Flowable.range(1, 10)
    .subscribe(
        item -> System.out.println("收到: " + item), // onNext
        error -> error.printStackTrace(),            // onError
        () -> System.out.println("完成"),            // onComplete
        subscription -> {
            // 这个参数就是 onSubscribe 接收的 Subscription!
            // 默认实现会调用 subscription.request(Long.MAX_VALUE)
            // 你可以自定义请求策略
            subscription.request(5); // 只请求5个
        }
    );

4.2、使用 subscribeWith()获取 Subscriber 实例

java 复制代码
Subscriber<Integer> mySubscriber = Flowable.range(1, 10)
    .subscribeWith(new Subscriber<Integer>() {
        @Override
        public void onSubscribe(Subscription s) {
            s.request(Long.MAX_VALUE);
        }
        
        @Override
        public void onNext(Integer integer) {
            System.out.println(integer);
        }
        
        @Override
        public void onError(Throwable t) {
            t.printStackTrace();
        }
        
        @Override
        public void onComplete() {
            System.out.println("完成");
        }
    });
    
// 可以稍后取消
// mySubscriber 实际上就是 DisposableSubscriber 或类似实现

五、内置的Subscriber实现

  1. DisposableSubscriber
java 复制代码
DisposableSubscriber<Integer> subscriber = new DisposableSubscriber<Integer>() {
    @Override public void onNext(Integer t) { System.out.println(t); }
    @Override public void onError(Throwable t) { t.printStackTrace(); }
    @Override public void onComplete() { System.out.println("完成"); }
};

Flowable.range(1, 10).subscribe(subscriber);
// 可以通过 subscriber.dispose() 取消
  1. ResourceSubscriber:支持资源管理的 Subscriber。
  2. DefaultSubscriber:基础的 Subscriber实现。

六、注意事项

  1. 必须调用 request():这是最重要的规则。忘记调用 request()是最常见的错误。
  2. request()是累加的:
java 复制代码
// 以下代码总共请求了 15 个数据
subscription.request(10);
subscription.request(5); // 不是"再要5个",而是"总共要15个"
  1. request()的调用位置:
    • 可以在 onSubscribe中调用(初始请求)
    • 可以在 onNext中调用(拉取更多)
    • 不要在 onError或 onComplete之后调用
    • 可以从任何线程调用(线程安全)
  2. 正确处理取消:
    • 调用 subscription.cancel()后,应停止调用 request()
    • 在 onError或 onComplete后,订阅自动结束,无需再调用 cancel()
  3. 避免在 onNext中阻塞:
    如果 onNext处理很慢,应该使用拉取模式(处理完一个再请求下一个),或使用异步处理。
  4. 资源清理:对于需要资源管理的场景(如数据库连接),考虑使用 ResourceSubscriber或在 onError/onComplete中清理资源。
java 复制代码
Flowable.create(emitter -> {
    // 快速生产数据
    for (int i = 0; i < 1000; i++) {
        if (emitter.isCancelled()) break; // 检查是否被取消
        emitter.onNext(i);
    }
    emitter.onComplete();
}, BackpressureStrategy.BUFFER) // 缓冲策略
.subscribe(new Subscriber<Integer>() {
    private Subscription subscription;
    private final int BATCH_SIZE = 10;
    
    @Override
    public void onSubscribe(Subscription s) {
        this.subscription = s;
        s.request(BATCH_SIZE); // 初始请求10个
    }
    
    @Override
    public void onNext(Integer integer) {
        System.out.println("处理: " + integer);
        
        // 模拟处理时间
        try { Thread.sleep(100); } catch (InterruptedException e) { }
        
        // 每处理完一个,检查是否需要请求更多
        // 这里实现一个简单的批处理:当处理完一批后请求下一批
        if (integer % BATCH_SIZE == BATCH_SIZE - 1) {
            subscription.request(BATCH_SIZE);
        }
    }
    
    @Override
    public void onError(Throwable t) {
        t.printStackTrace();
    }
    
    @Override
    public void onComplete() {
        System.out.println("所有数据处理完成");
    }
});
相关推荐
米羊1215 小时前
ThinkPHP 漏洞(下)
android
前路不黑暗@5 小时前
Java项目:Java脚手架项目的 B 端用户服务(十四)
android·java·开发语言·spring boot·笔记·学习·spring cloud
Rainman博6 小时前
AMS-Activity启动流程
android
恋猫de小郭6 小时前
Flutter 设计包解耦新进展,material_ui 和 cupertino_ui 发布预告
android·前端·flutter
blackorbird10 小时前
新型Keenadu安卓固件级后门揭开跨僵尸网络协同攻击链条
android·网络
前路不黑暗@10 小时前
Java项目:Java脚手架项目的阿里云短信服务集成(十六)
android·java·spring boot·学习·spring cloud·阿里云·maven
吴声子夜歌10 小时前
RxJava——Flowable与背压
android·java·rxjava
L-李俊漩11 小时前
Android studio修改gradle路径
android·android studio
九狼JIULANG12 小时前
基于Flutter+Riverpod+MVI 实现的跨平台「AI 提示词优化工具」
android·开源·github