RxJava里doOnNext的使用和线程处理

本文探讨了RxJava中doOnNext操作符的应用场景及其如何处理数据流中的副作用问题。通过实例展示了如何利用doOnNext进行调试、缓存网络结果,并解决了耗时操作阻碍主线程显示数据的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

doOnNext的使用

我对doOnNext的使用是存在疑惑的,按照官方文档

The doOnNext operator is much like doOnEach(Action1) except that the Action that you pass it as a parameter does not accept a Notification but instead simply accepts the emitted item.

更多的文档里说明do系列的作用是side effect,当onNext发生时,它被调用,不改变数据流。我做的测试代码:

2017.9.28增加修正:
首先要理解什么是副作用:方法执行的时候,产生了外部可以观察到的变化就是产生了副作用。
那些文档的意思是说,不要在doOnNext做能发生副作用的方法,也就是不要去改变数据流

Observable.create(new Observable.OnSubscribe<Person>() {
            @Override
            public void call(Subscriber<? super Person> subscriber) {
                Person person = new Person(201);
                subscriber.onNext(person);
            }
        }).doOnNext(new Action1<Person>() {
            @Override
            public void call(Person person) {
                person.age = 301;
            }
        }).subscribe(new Action1<Person>() {
            @Override
            public void call(Person person) {
                Log.d(TAG, "call: " + person.age);//输出301
            }
        });

可见,doOnNext是改变了流里的数据的,所以并不明白不改变数据流是什么意思。
从github上很多项目这篇文章来看,do系列的作用

  • 使用doOnNext()来调试
  • 在flatMap()里使用doOnError()作为错误处理。
  • 使用doOnNext()去保存/缓存网络结果

    按照我的测试

   final SimpleDateFormat sDateFormat    =   new   SimpleDateFormat("yyyy-MM-dd    hh:mm:ss");
        Observable.create(new Observable.OnSubscribe<Person>() {
            @Override
            public void call(Subscriber<? super Person> subscriber) {
                String    date    =    sDateFormat.format(new    Date());
                System.out.println(date + " call " + Thread.currentThread().getName());
                Person person = new Person(201);
                subscriber.onNext(person);
            }
        }).subscribeOn(Schedulers.io()) //指定耗时进程
                .observeOn(Schedulers.newThread()) //指定doOnNext执行线程是新线程
                .doOnNext(new Action1<Person>() {
            @Override
            public void call(Person person) {
                String    date    =    sDateFormat.format(new    Date());
                System.out.println(date + " call " + Thread.currentThread().getName());
                person.age = 301;
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).observeOn(AndroidSchedulers.mainThread())//指定最后观察者在主线程
                .subscribe(new Action1<Person>() {
            @Override
            public void call(Person person) {
                String    date    =    sDateFormat.format(new    Date());
                System.out.println(date + " call " + Thread.currentThread().getName());
                Log.d(TAG, "call: " + person.age);
            }
        });

执行结果

03-01 14:49:29.897 23442-24145/com.example.myrxlearn I/System.out: 2016-03-01    02:49:29 call RxCachedThreadScheduler-2
03-01 14:49:29.907 23442-24144/com.example.myrxlearn I/System.out: 2016-03-01    02:49:29 call RxNewThreadScheduler-2
03-01 14:49:31.907 23442-23442/com.example.myrxlearn I/System.out: 2016-03-01    02:49:31 call main

也就是说直到doOnNext里的方法在新线程执行完毕,subscribe里的call才有机会在主线程执行。

一直没看到有合适的方法解决这个问题,因为缓存的时间不应该去阻碍主线程里数据的显示。

今天做回顾时看到了这篇文章

非阻塞I/O操作

现在我们可以使用Schedulers.io()创建非阻塞的版本:

public static void storeBitmap(Context context, Bitmap bitmap,
String filename) {
Schedulers.io().createWorker().schedule(() -> {
blockingStoreBitmap(context, bitmap, filename);
}); }

然后想起来一直存在的这个问题。只需要把上面的代码改成

.doOnNext(new Action1<Person>() {
            @Override
            public void call(Person person) {
                String    date    =    sDateFormat.format(new    Date());
                System.out.println(date + " call " + Thread.currentThread().getName());
                person.age = 301;
                Schedulers.io().createWorker().schedule(new Action0() {
                    @Override
                    public void call() {
                        try {
                            Thread.sleep(2000);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        })

不需要在用observeOn指定在新线程就可以实现

03-01 14:55:02.307 30368-30406/com.example.myrxlearn I/System.out: 2016-03-01    02:55:02 call RxCachedThreadScheduler-1
03-01 14:55:02.307 30368-30406/com.example.myrxlearn I/System.out: 2016-03-01    02:55:02 call RxCachedThreadScheduler-1
03-01 14:55:02.347 30368-30368/com.example.myrxlearn I/System.out: 2016-03-01    02:55:02 call main

这样doOnNext可以方便的用来调试,用来缓存。
奈何,我还是没能明白副作用,期待指点。

doOnNext is for side-effects: you want to react (eg. log) to item
emissions in an intermediate step of your stream, for example before
the stream is filtered, for transverse behavior like logging, but you
still want the value to propagate down the stream.

onNext is more final, it consumes the value.

### 关于RxJava中的线程配置 在RxJava中,并不需要显式地手动设置线程的数量来处理任务调度。相反,RxJava提供了一系列预定义的调度器(Scheduler),它们能够有效地管理线程资源并执行异步操作[^3]。 对于需要自定义行为的情况,比如想要控制并发度或者指定特定类型的线程池(如固定大小或缓存型),可以通过`Schedulers.from(Executor)`的方式传入一个由开发者自己构建的`ExecutorService`实例。这允许更加灵活地调整底层线程的行为以适应具体应用场景的需求[^4]。 当涉及到正确配置线程池时,建议遵循以下原则: - **评估需求**:根据应用程序的具体工作负载特性决定合适的线程数目。 - **选择适当类型**:依据任务性质选用适合的线程池种类,例如对于I/O密集型任务可以选择具有较大容量的工作队列;而对于CPU密集型则应保持较小规模但高效的线程集合。 - **利用现有工具**:尽可能依赖经过良好测试的标准库组件而非自行实现复杂的逻辑。 下面是一个简单的例子展示如何使用自定义线程池配合RxJava进行任务分发: ```java import io.reactivex.rxjava3.core.Observable; import java.util.concurrent.Executors; public class Example { public static void main(String[] args) throws InterruptedException { // 创建一个拥有两个核心线程线程池 var executor = Executors.newFixedThreadPool(2); Observable.just("Task A", "Task B") .subscribeOn(Schedulers.from(executor)) // 使用自定义线程池作为订阅者所在的上下文环境 .doOnNext(task -> System.out.println(Thread.currentThread().getName() + ": Executing " + task)) .blockingSubscribe(); Thread.sleep(100); // 确保主线程不会提前结束 executor.shutdown(); // 安全关闭线程池 } } ``` 此代码片段展示了如何创建一个固定的线程池并通过`Schedulers.from()`将其应用于Observable的操作链中,从而实现了对线程使用的精确控制.
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值