android RXJava2.0(二)--线程控制

RxJava 三:RxJava 3 线程调度 目录 1.Scheduler类型 2.Scheduler线程控制接口: 3.Schedules线程自由切换 4.延伸:利用subscribeOn() 指定doOnSubscribe()执行线程 RxJava 默认事件的产生和消费都是在同一个线程的。也就是说,默认实现的只是一个同步的观察者模式,即:在哪个线程调用subscribe()(订阅),就在哪个线程生产事件;在哪个线程生产事件,就在哪个线程消费事件,此时数据发送和数据接收是一一对应的,即发送一个,接收一个,再发送一个,再接收一个……。如... 阅读详情

正题

还是以之前的例子, 两根水管:

正常情况下, 上游和下游是工作在同一个线程中的, 也就是说上游在哪个线程发事件, 下游就在哪个线程接收事件.

怎么去理解呢, 以Android为例, 一个Activity的所有动作默认都是在主线程中运行的, 比如我们在onCreate中打出当前线程的名字:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, Thread.currentThread().getName());
    }

结果便是:

D/TAG: main

回到RxJava中, 当我们在主线程中去创建一个上游Observable来发送事件, 则这个上游默认就在主线程发送事件.

当我们在主线程去创建一个下游Observer来接收事件, 则这个下游默认就在主线程中接收事件, 来看段代码:

@Override                                                                                       
protected void onCreate(Bundle savedInstanceState) {                                            
    super.onCreate(savedInstanceState);                                                         
    setContentView(R.layout.activity_main);                                                     

    Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {   
        @Override                                                                               
        public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {            
            Log.d(TAG, "Observable thread is : " + Thread.currentThread().getName());           
            Log.d(TAG, "emit 1");                                                               
            emitter.onNext(1);                                                                  
        }                                                                                       
    });                                                                                         

    Consumer<Integer> consumer = new Consumer<Integer>() {                                      
        @Override                                                                               
        public void accept(Integer integer) throws Exception {                                  
            Log.d(TAG, "Observer thread is :" + Thread.currentThread().getName());              
            Log.d(TAG, "onNext: " + integer);                                                   
        }                                                                                       
    };                                                                                          

    observable.subscribe(consumer);                                                             
}

在主线程中分别创建上游和下游, 然后将他们连接在一起, 同时分别打印出它们所在的线程, 运行结果为:

D/TAG: Observable thread is : main
D/TAG: emit 1                     
D/TAG: Observer thread is :main   
D/TAG: onNext: 1

这就验证了刚才所说, 上下游默认是在同一个线程工作.

这样肯定是满足不了我们的需求的, 我们更多想要的是这么一种情况, 在子线程中做耗时的操作, 然后回到主线程中来操作UI, 用图片来描述就是下面这个图片:

在这个图中, 我们用黄色水管表示子线程, 深蓝色水管表示主线程.

要达到这个目的, 我们需要先改变上游发送事件的线程, 让它去子线程中发送事件, 然后再改变下游的线程, 让它去主线程接收事件. 通过RxJava内置的线程调度器可以很轻松的做到这一点. 接下来看一段代码:

@Override                                                                                       
protected void onCreate(Bundle savedInstanceState) {                                            
    super.onCreate(savedInstanceState);                                                         
    setContentView(R.layout.activity_main);                                                     

    Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {   
        @Override                                                                               
        public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {            
            Log.d(TAG, "Observable thread is : " + Thread.currentThread().getName());           
            Log.d(TAG, "emit 1");                                                               
            emitter.onNext(1);                                                                  
        }                                                                                       
    });                                                                                         

    Consumer<Integer> consumer = new Consumer<Integer>() {                                      
        @Override                                                                               
        public void accept(Integer integer) throws Exception {                                  
            Log.d(TAG, "Observer thread is :" + Thread.currentThread().getName());              
            Log.d(TAG, "onNext: " + integer);                                                   
        }                                                                                       
    };                                                                                          

    observable.subscribeOn(Schedulers.newThread())                                              
            .observeOn(AndroidSchedulers.mainThread())                                          
            .subscribe(consumer);                                                               
}

还是刚才的例子, 只不过我们太添加了一点东西, 先来看看运行结果:

 D/TAG: Observable thread is : RxNewThreadScheduler-2  
 D/TAG: emit 1                                         
 D/TAG: Observer thread is :main                       
 D/TAG: onNext: 1

可以看到, 上游发送事件的线程的确改变了, 是在一个叫 RxNewThreadScheduler-2的线程中发送的事件, 而下游仍然在主线程中接收事件, 这说明我们的目的达成了, 接下来看看是如何做到的.

和上一段代码相比,这段代码只不过是增加了两行代码:

.subscribeOn(Schedulers.newThread())                                              
.observeOn(AndroidSchedulers.mainThread())

作为一个初学者的入门教程, 并不会贴出一大堆源码来分析, 因此只需要让大家记住几个要点, 已达到如何正确的去使用这个目的才是我们的目标.

简单的来说, subscribeOn() 指定的是上游发送事件的线程, observeOn() 指定的是下游接收事件的线程.

多次指定上游的线程只有第一次指定的有效, 也就是说多次调用subscribeOn() 只有第一次的有效, 其余的会被忽略.

多次指定下游的线程是可以的, 也就是说每调用一次observeOn() , 下游的线程就会切换一次.

举个例子:

observable.subscribeOn(Schedulers.newThread())     
         .subscribeOn(Schedulers.io())              
         .observeOn(AndroidSchedulers.mainThread()) 
         .observeOn(Schedulers.io())                
         .subscribe(consumer);

这段代码中指定了两次上游发送事件的线程, 分别是newThread和IO线程, 下游也指定了两次线程,分别是main和IO线程. 运行结果为:

D/TAG: Observable thread is : RxNewThreadScheduler-3
D/TAG: emit 1                                       
D/TAG: Observer thread is :RxCachedThreadScheduler-1
D/TAG: onNext: 1

可以看到, 上游虽然指定了两次线程, 但只有第一次指定的有效, 依然是在 RxNewThreadScheduler 线程中, 而下游则跑到了 RxCachedThreadScheduler 中, 这个 CacheThread 其实就是 IO 线程池中的一个.

为了更清晰的看到下游的线程切换过程, 我们加点log:

observable.subscribeOn(Schedulers.newThread())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnNext(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer integer) throws Exception {
                        Log.d(TAG, "After observeOn(mainThread), current thread is: " + Thread.currentThread().getName());
                    }
                })
                .observeOn(Schedulers.io())
                .doOnNext(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer integer) throws Exception {
                        Log.d(TAG, "After observeOn(io), current thread is : " + Thread.currentThread().getName());
                    }
                })
                .subscribe(consumer);

我们在下游线程切换之后, 把当前的线程打印出来, 运行结果:

D/TAG: Observable thread is : RxNewThreadScheduler-1                                             
D/TAG: emit 1                                                                                    
D/TAG: After observeOn(mainThread), current thread is: main                                      
D/TAG: After observeOn(io), current thread is : RxCachedThreadScheduler-2                        
D/TAG: Observer thread is :RxCachedThreadScheduler-2                                             
D/TAG: onNext: 1

可以看到, 每调用一次 observeOn() 线程便会切换一次, 因此如果我们有类似的需求时, 便可知道如何处理了.

在RxJava中, 已经内置了很多线程选项供我们选择, 例如有

  • Schedulers.io() 代表io操作的线程, 通常用于网络,读写文件等io密集型的操作
  • Schedulers.computation() 代表CPU计算密集型的操作, 例如需要大量计算的操作
  • Schedulers.newThread() 代表一个常规的新线程
  • AndroidSchedulers.mainThread() 代表Android的主线程
    这些内置的Scheduler已经足够满足我们开发的需求, 因此我们应该使用内置的这些选项, 在RxJava内部使用的是线程池来维护这些线程, 所有效率也比较高.

实践

对于我们Android开发人员来说, 经常会将一些耗时的操作放在后台, 比如网络请求或者读写文件,操作数据库等等,等到操作完成之后回到主线程去更新UI, 有了上面的这些基础, 那么现在我们就可以轻松的去做到这样一些操作.

下面来举几个常用的场景.

网络请求

Android中有名的网络请求库就那么几个, Retrofit能够从中脱颖而出很大原因就是因为它支持RxJava的方式来调用, 下面简单讲解一下它的基本用法.

要使用Retrofit,先添加Gradle配置:

//retrofit
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    //Gson converter
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
    //RxJava2 Adapter
    compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
    //okhttp
    compile 'com.squareup.okhttp3:okhttp:3.4.1'
    compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'

随后定义Api接口:

public interface Api {
    @GET
    Observable<LoginResponse> login(@Body LoginRequest request);

    @GET
    Observable<RegisterResponse> register(@Body RegisterRequest request);
}

接着创建一个Retrofit客户端:

private static Retrofit create() {
            OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
            builder.readTimeout(10, TimeUnit.SECONDS);
            builder.connectTimeout(9, TimeUnit.SECONDS);

            if (BuildConfig.DEBUG) {
                HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
                interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
                builder.addInterceptor(interceptor);
            }

            return new Retrofit.Builder().baseUrl(ENDPOINT)
                    .client(builder.build())
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .build();
}

发起请求就很简单了:

    Api api = retrofit.create(Api.class);
    api.login(request)
          .subscribeOn(Schedulers.io())               //在IO线程进行网络请求
         .observeOn(AndroidSchedulers.mainThread())  //回到主线程去处理请求结果
        .subscribe(new Observer<LoginResponse>() {
                @Override
            public void onSubscribe(Disposable d) {}

            @Override
            public void onNext(LoginResponse value) {}

            @Override
            public void onError(Throwable e) {
                Toast.makeText(mContext, "登录失败", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onComplete() {
                Toast.makeText(mContext, "登录成功", Toast.LENGTH_SHORT).show();
            }
        });

看似很完美, 但我们忽略了一点, 如果在请求的过程中 Activity 已经退出了, 这个时候如果回到主线程去更新 UI, 那么 APP 肯定就崩溃了, 怎么办呢, 上一节我们说到了 Disposable , 说它是个开关, 调用它的 dispose() 方法时就会切断水管, 使得下游收不到事件, 既然收不到事件, 那么也就不会再去更新UI了. 因此我们可以在 Activity 中将这个 Disposable 保存起来, 当 Activity 退出时, 切断它即可.

那如果有多个 Disposable 该怎么办呢, RxJava 中已经内置了一个容器 CompositeDisposable , 每当我们得到一个 Disposable 时就调用 CompositeDisposable.add() 将它添加到容器中, 在退出的时候, 调用 CompositeDisposable.clear() 即可切断所有的水管.

读写数据库

上面说了网络请求的例子, 接下来再看看读写数据库, 读写数据库也算一个耗时的操作, 因此我们也最好放在IO线程里去进行, 这个例子就比较简单, 直接上代码:

public Observable<List<Record>> readAllRecords() {
        return Observable.create(new ObservableOnSubscribe<List<Record>>() {
            @Override
            public void subscribe(ObservableEmitter<List<Record>> emitter) throws Exception {
                Cursor cursor = null;
                try {
                    cursor = getReadableDatabase().rawQuery("select * from " + TABLE_NAME, new String[]{});
                    List<Record> result = new ArrayList<>();
                    while (cursor.moveToNext()) {
                        result.add(Db.Record.read(cursor));
                    }
                    emitter.onNext(result);
                    emitter.onComplete();
                } finally {
                    if (cursor != null) {
                        cursor.close();
                    }
                }
            }
        }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
    }

好了本次的教程就到这里吧, 后面的教程将会教大家如何使用RxJava中强大的操作符. 通过使用这些操作符可以很轻松的做到各种吊炸天的效果. 敬请期待.

自己总结


  1. 线程切换
    1. 一次onSubscribe
    2. 多次onObserver
  2. 使用dispose()切断下游

mDisposable.dispose();
// mCompositeDisposable.clear(); //当有多个disposabele的时候全部清空

RxJava2.0线程调度深入解析与实战教程 在Android开发中,异步编程已成为构建高性能、高响应性应用的核心手段,而RxJava2.0作为响应式编程库的代表,其线程调度机制则是实现高效异步任务处理的关键。通过与两个核心方法,开发者可以灵活控制任务的执行线程与结果的消费线程。本章将从整体视角切入,剖析线程调度在RxJava2中的作用原理,帮助开发者建立对线程切换流程的初步认知,为后续章节的深入分析打下坚实基础。RxJava2 提供了一组内置的调度器(Schedulers),用于控制任务的执行线程。 阅读详情

相关推荐

2023年5月青少年软件编程Python等级考试四级真题(含答案和解析)

电子学会,2023年5月,青少年软件编程,Python等级考试,四级真题(含答案和解析)

m0_37554648的博客 903

RxJavaCallAdapterFactory默认实现异步调度

设计给Retrofit代理的Flowable 和 Observable的对象默认添加异步调度 经常重复的异步调度代码: Sevice.XXX() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(); 根据RxJava...

诺诺诺诺诺诺诺诺的博客 2万+

VBA中如何使用Edge内核Browser?

在微软宣布停止对IE的支持后,许多使用VBA中Web Browser的用户遇到了困境,因为Web Browser使用的IE内核。然而微软并没有给出支持Edge的新版Web Browser,网上许多第三方的库也不够完善。经过调研,libEdge可以较好的和VBA集成,实现在Excel中打开Browser,甚至能够通过执行js代码,获取web页面的信息。项目需要WebView2Loader.dll和LibEdge.dll两个库,区分x86和x64版本。

金木AI 2396

给初学者的RxJava2.0教程(二)

Outline [TOC] 前言 上一节教程讲解了最基本的RxJava2的使用, 在本节中, 我们将学习RxJava强大的线程控制. 正题 还是以之前的例子, 两根水管: RxJava 正常情况下, 上游和下游是工作在同一个线程中的, 也就是说上游在哪个线程发事件, 下游就在哪个线程接收事件. 怎么去理解呢, 以Android为例, 一个Activity的所有动作默认都是在主线程中运行的, ...

weixin_33957648的博客 239

OpenEuler内核编译及替换

一、内核源码下载 在gitee上下载OpenEuler内核源码 下载地址 选择需要的内核版本源码压缩包进行下载 二、内核编译 1.解压缩 将下载好的源码进行解压 tar xvf openeuler-kernel-4.19.90-2107.1.0.tar.gz 2.清理源代码树 解压后得到一个名为kernel的内核源码文件夹,进入后执行命令 make mrproper 清理过去内核编译产生的config,第一次编译时可不执行此命令 3.生成内核配置文件.config 先将将系统原配置文件拷贝过来,原

m0_56602092的博客 2万+

华为C语言笔试题(附答案)

华为C语言笔试题(附答案)     1、局部变量能否和全局变量重名?   答:能,局部会屏蔽全局。要用全局变量,需要使用 ":: "   局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内。  

峰云四起 3031

Retrofit2<二> RxJava2CallAdapter分析

首先承接上面一篇文章。分析其创建过程 public static RxJava2CallAdapterFactory create() { return new RxJava2CallAdapterFactory(null, false); } 默认是不传入参数。继续看构造方法。 private RxJava2CallAdapterFactory(@Nullable Scheduler scheduler, boolean isAsync) { this.scheduler = sched

d_o_n_g2的博客 972

Debezium系列之:使用指定值替代指定字段的值

Debezium系列之:使用指定值替代指定字段的值

zhengzaifeidelushang的博客 306

我就简单理解一下Retrofit及其CallAdapter.Factory

前言 用了Retrofit这么久 ,感觉Retrofit的架构设计牛逼,是学习设计思想和设计模式的不二典范。但今天不是来详细分析这个框架的原理,主要简单了解下Retrofit的CallAdapter.Factory及怎么通过CallAdapter.Factory兼容OkHttp和RxJava。 OkHttpClient.Builder httpClient = new ...

Kelly 6829

SAP-ABAP:FI财务会计模块常用BAPI及核心用途实例说明

SAP FI模块的BAPI覆盖了从主数据管理、凭证过账到财务关账的全流程操作,是系统集成与自动化的重要工具。实际应用中需结合业务场景选择合适的BAPI,并严格遵循事务控制与校验规则。如需扩展功能,可通过BAdI或用户出口增强标准逻辑。

baidu_35680696的博客 1600

Android AVB 分析(十三)dm-verity 设备是如何映射的?

在上一篇《Android AVB 分析(十二)嵌入式设备安全中的 dm-verity 简介》介绍 dm-verity 原理的时候,作者提供了一个 dm-verity 演示的例子。本篇我们基于 AOSP 源码 `android-13.0.0_r41` 实际编译生成的 Google Pixel 7 (“panther”) 设备的 system 分区镜像进行 dm-verity 设备验证实战。

洛奇看世界 2437

Retrofit源码解读(三)--RxJavaCallAdapterFactory讲解

Retrofit源码解读(三)–RxJavaCallAdapterFactory讲解标签

wuyinlei的博客 1709

基于Requests的百度图片搜索爬取技术详解

本文介绍了一种高效获取百度图片搜索数据的混合请求爬虫技术。该技术结合HTML解析与JSON API调用,通过分析百度图片搜索的网络通信模式,实现了首页HTML解析获取初始参数、后续页面直接调用JSON接口的双重机制。系统采用模块化设计,包含Cookies解析、智能请求分发、数据提取、图片下载等核心模块,支持自定义关键词和爬取页数配置。关键技术包括动态参数管理、正则表达式提取、JSON数据处理等,相比传统爬虫方法具有效率高、资源消耗低的优势,为计算机视觉研究提供了可靠的大规模图片数据采集方案。

那些看似不起波澜的日复一日,会在某天让你看到坚持的意义 419

【Android -- RxJava】RxJava2.0 教程(二),强大的线程控制

在本节中, 我们将学习 RxJava 强大的线程控制。

Kevin Learn Android 733

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Android RxJava第一弹之原理详解、使用详解、常用场景(基于Rxjava2.0)

Android RxJava第一弹之原理详解、使用详解、常用场景(基于Rxjava2.0) Android RxJava第二弹之RxJava封装库 RxJava+Animation RxJava+Glide Android RxJava第三弹之RxJava2.0尝鲜本人参考以下文章给 Android 开发者的 RxJava 详解—扔物线Rxjava 2.0 与 Rxjava 1.0有什么不同

qq_28195645的博客 9425

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Android开发之路--Rxjava2.0的使用

文章目录一、前言1、RxJava是什么?2、为什么要使用Rxjava二、Rxjava的使用1、依赖配置2、基本概念3、具体使用3.1 创建被观察者3.2 创建观察者3.3 订阅3.4 清爽的链式调用4、操作符4.1 创建操作符4.1.1 create()4.1.2 just()4.1.3 from操作符a:fromArray操作符b:fromCallable操作符c:fromFuture操作符d:...

飞兔的博客 2866

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

RxJava-2-0还没熟悉,RxJava-3-0说来就来了!(基础篇)

如果你觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。,第一时间获取最新知识点Android架构师之路很漫长,一起共勉吧!

2401_84132544的博客 993

RxJava在Android中的使用详解

本文详细讲解Rxjava在Android中的原理,技术及应用。比较简单(本文转载于RxJava在Android中的使用)。 前言 我从去年开始使用 RxJava ,到现在一年多了。今年加入了 Flipboard 后,看到 Flipboard 的 Android 项目也在使用 RxJava ,并且使用的场景越来越多 。而最近这几个月,我也发现国内越来越多的人开始提及 RxJava 。有人说『RxJava 真是太好用了』,有人说『RxJava 真是太难用了』,另外更多的人表示:我真的百度了也谷歌了,但我还是

AndyYuan317的博客 3222

RxJava2 系列-1:一篇的比较全面的 RxJava2 方法总结

看了许多讲解RxJava的文章,有些文章讲解的内容是基于第一个版本的,有些文章的讲解是通过比较常用的一些API和基础的概念进行讲解的。 但是每次看到RxJava的类中的几十个方法的时候,总是感觉心里没底。所以,我打算自己去专门写篇文章来从API的角度系统地梳理一下RxJava的各种方法和用法。 1、RxJava 基本 1.1 RxJava 简介 RxJava是一个在Java VM上使用可观测的序列...

你好,开发者 1472

RxJava2.0教程(二)

前言 上一节教程讲解了最基本的RxJava2的使用, 在本节中, 我们将学习RxJava强大的线程控制. 正题 还是以之前的例子, 两根水管: 正常情况下, 上游和下游是工作在同一个线程中的, 也就是说上游在哪个线程发事件, 下游就在哪个线程接收事件. 怎么去理解呢, 以Android为例, 一个Activity的所有动作默认都是在主线程中运行的, 比如我们在onCreate中打出当前线程的名字: @Override protected void onCreate(Bundle savedInstance

chengxuyuan22的博客 154

【Android -- RxJava】RxJava2.0 教程(九),一份详细的 RxJava 学习指南

一份详细的 RxJava 学习指南。

Kevin Learn Android 978

我的android多线程编程之路(2)之RxJava Schedulers源码分析

写在伊始上一篇介绍了线程的一些基础知识和工作这么久以后对于多线程部分的使用经验之路,这篇主要对RxJava线程控制部分进行分析。RxJava(本文就RxJava2.0分析)说实话,近一年多一直在用rxjava进行项目架构的编写及封装及一些异步请求的处理等等。真的很好用,但本文只对其线程部分进行分析。如果你想学习rxjava的话,推荐您看一下如下几篇文档,也是一点一点学过来的,希望可以帮到您。http

say_from_wen的博客 1783

Android-0.RxJava2简介

RxJava2简介

hgy413的专栏 1031
上一篇: android 签名打包时去掉log日志打印(仅在debug包中打印log信息)
下一篇: View学习(一)--坐标系
missingcall
博客等级 码龄10年 8粉丝 40原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值