www.icesr.com
IT运维工程师的摇篮

RxJava简易上手指南

RxJava简易上手指南

标签(空格分隔): android rxjava

作者:陈小默

版权声明:禁止商用,转载请注明出处


本文章仅作为初学者最快上手实践,不会深入涉及代码原理,有兴趣的朋友可以参阅 扔物线-给Android开发者的RxJava详解

一、介绍

RxJava通过扩展的观察者模式实现异步操作

Observable:被观察者
Observer:观察者
Subscriber:消息订阅者

其中的关系是观察者(Observer)或者消息订阅者(Subscriber)通过订阅(subscribe)的方式观察被观察者(Observable)的行为

二、回调方法介绍

2.1 观察者对象

<code>        <span class="hljs-comment">//观察者对象 实现了观察者接口,其中泛型代表了观察者需要的相应对象</span>
        Observer&lt;String&gt; observer = <span class="hljs-keyword">new</span> Observer&lt;String&gt;() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCompleted</span>() {
                <span class="hljs-comment">//观察任务完成时回调</span>
            }

            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onError</span>(Throwable e) {
                <span class="hljs-comment">//发生错误时回调,并即刻终止事件序列的传递(与onCompleted方法互斥)</span>
            }

            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onNext</span>(String s) {
                <span class="hljs-comment">//监听事件序列回调</span>
            }
        };</code>

2.2 消息订阅者对象

消息订阅者是实现了观察者接口的抽象类。如果使用观察者对象,在运行时仍会被包装为消息订阅这对象运行

<code>        <span class="hljs-comment">//消息订阅者对象</span>
        Subscriber&lt;Bitmap&gt; subscriber = <span class="hljs-keyword">new</span> Subscriber&lt;Bitmap&gt;() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onStart</span>() {
                <span class="hljs-comment">//当消息订阅动作完成时,自动回调此方法</span>
                <span class="hljs-comment">//此方法运行在订阅行为发生的线程</span>
                <span class="hljs-comment">//不安全,建议初始化方法使用Observable.doOnSubscribe()</span>
            }

            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCompleted</span>() {

            }

            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onError</span>(Throwable e) {

            }

            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onNext</span>(Bitmap bitmap) {

            }
        };</code>

2.3 观察对象

该对象代表被观察者的行为,并将行为告知订阅者

<code>        <span class="hljs-comment">//观察行为对象</span>
        Observable.OnSubscribe&lt;String&gt; onSubscribe = <span class="hljs-keyword">new</span> Observable.OnSubscribe&lt;String&gt;() {
            <span class="hljs-javadoc">/**
             * 在此方法将行为通知给观察者对象
             *<span class="hljs-javadoctag"> @param</span> subscriber 框架传入的观察者对象
             */</span>
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">call</span>(Subscriber&lt;? <span class="hljs-keyword">super</span> String&gt; subscriber) {
                subscriber.onNext(<span class="hljs-string">"Hello "</span>);
                subscriber.onNext(<span class="hljs-string">"World "</span>);
                subscriber.onCompleted();<span class="hljs-comment">//此方法必须被调用,代表事件结束</span>
            }
        };</code>

2.4 不完整定义回调

以下三个对象分别代表观察者对象的三种行为,使用时可以根据需求完成部分回调

<code>        <span class="hljs-comment">//话说这些接口的名字太不友好了</span>
        Action1&lt;String&gt; onNext = <span class="hljs-keyword">new</span> Action1&lt;String&gt;() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">call</span>(String s) {
                <span class="hljs-comment">//监听事件序列回调</span>
            }
        };
        Action1&lt;Throwable&gt; onError = <span class="hljs-keyword">new</span> Action1&lt;Throwable&gt;() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">call</span>(Throwable s) {
                <span class="hljs-comment">//发生错误时回调,并即刻终止事件序列的传递</span>
            }
        };
        Action0 onComplete = <span class="hljs-keyword">new</span> Action0() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">call</span>() {
                <span class="hljs-comment">//观察任务完成时回调</span>
            }
        };</code>

三、基本实现

<code>        <span class="hljs-comment">//加载观察对象</span>
        Observable&lt;String&gt; observable = Observable.create(onSubscribe);

        <span class="hljs-comment">//加载消息订阅者(观察者)对象,此时立即调用onStart()方法</span>
        observable.subscribe(observer);

        <span class="hljs-comment">//不使用消息订阅者对象,采用不完整定义(任选其一)</span>
        observable.subscribe(onNext);
        observable.subscribe(onNext,onError);
        observable.subscribe(onNext,onError,onComplete);</code>

3.1 观察者对象的加载

加载观察对象的方式还有另外两种形式

3.1.1 from

<code>        <span class="hljs-comment">//传入数组</span>
        String[] sList= {<span class="hljs-string">"Hello "</span>,<span class="hljs-string">"World "</span>};
        observable = Observable.from(sList);</code>

3.1.2 just

<code>        <span class="hljs-comment">//看到这个方法的若干重载的实现,都开始怀疑人生了,难道RxJava诞生的时候变长参数还没有支持?</span>
        observable = Observable.just(<span class="hljs-string">"Hello "</span>,<span class="hljs-string">"World "</span>);</code>

四、线程调度介绍

在不使用线程调度的情况下,观察者和被观察者处于同一线程当中,如果想要切换线程则必须使用线程调度方法指挥观察者的运行环境,先来介绍几个参数:

  • Schedulers.immediate();没有线程切换过程,事件在哪里产生就在哪里消费
  • Schedulers.newThread();将当前事件切换到新建线程中执行
  • Schedulers.io();同样也是开启新线程,但是这里会有线程的复用,建议将耗时操作(文件读写,网络请求,数据库读写)在这里执行。
  • Schedulers.computation();固定线程池,用来处理计算密集型操作(图形渲染等),不建议将耗时操作放在此线程中执行,影响效率。你要问为什么,回去看看你操作系统的课本或者参阅博客 浅谈Java两种并发类型——计算密集型与IO密集型

到底如何进行线程调度呢?
这里介绍两个方法:subscribeOn() 和 observeOn(),这些方法可以多次调用,用来指定其后传入对象的执行状态

  • subscribeOn():订阅者线程(事件产生线程)该方法用来指定订阅操作执行过程所在的线程,既观察对象中call方法运行所在的线程。
  • observeOn():观察者线程(事件消费线程)指定其后传入的观察者对象或者消息订阅者对象中三个主要方法运行所在的线程。

代码

<code>        <span class="hljs-comment">// 以下调用了两次在不同线程状态切换</span>
        observable.subscribeOn(Schedulers.newThread())<span class="hljs-comment">//订阅行为运行在子线程</span>
                .observeOn(AndroidSchedulers.mainThread())<span class="hljs-comment">//观察者运行在UI线程</span>
                .subscribe(subscriber);

        observable.subscribeOn(Schedulers.computation())<span class="hljs-comment">//订阅行为运行在子线程</span>
                .observeOn(Schedulers.io())<span class="hljs-comment">//观察者运行在IO线程</span>
                .subscribe(observer);
</code>

五、变换的基本介绍

常用的变换有两种 map()和flatMap(),其中的参数都是Func1接口的实现类。
变换的call方法执行在消息订阅者线程

5.1 Func1接口的两种实现方式

该接口用于泛型所指定的参数的变换

5.1.1 Map变幻对象

<code>        Func1&lt;String , Bitmap&gt; loadImage = <span class="hljs-keyword">new</span> Func1&lt;String, Bitmap&gt;() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> Bitmap <span class="hljs-title">call</span>(String url) {
                <span class="hljs-comment">//在这里完成网络请求加载图片并返回</span>
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
            }
        };
        observable.map(loadImage)
            .subscribe(observer);</code>

5.1.2 FlatMap变幻对象

上面的Map变换对象还算是比较好理解,但是下面的faltMap方法就不那么好理解了,下面用一个获取学生上课的班级信息的例子来演示:

定义班级信息

<code>        <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ClassInfo</span> {</span>
            <span class="hljs-keyword">private</span> String className;<span class="hljs-comment">//班级名</span>
            ...getter &amp; setter
        }</code>

定义学生信息

<code>        <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Student</span> {</span>
            <span class="hljs-keyword">private</span> List&lt;ClassInfo&gt; classes;
            ...getter &amp; setter
        }</code>

下面模拟获取学生班级名称

<code>        Observable.just(<span class="hljs-keyword">new</span> Student(), <span class="hljs-keyword">new</span> Student())
                .flatMap(<span class="hljs-keyword">new</span> Func1&lt;Student, Observable&lt;String&gt;&gt;() {

                    <span class="hljs-comment">//第一次变换,从student对象中取出教室信息列表</span>
                    <span class="hljs-annotation">@Override</span>
                    <span class="hljs-keyword">public</span> Observable&lt;String&gt; <span class="hljs-title">call</span>(Student student) {
                        <span class="hljs-keyword">return</span> Observable.from(student.getClasses())
                                .map(<span class="hljs-keyword">new</span> Func1&lt;ClassInfo, String&gt;() {

                                    <span class="hljs-comment">//第二次变换,从ClassInfo对象取出班级名称</span>
                                    <span class="hljs-annotation">@Override</span>
                                    <span class="hljs-keyword">public</span> String <span class="hljs-title">call</span>(ClassInfo classInfo) {
                                        <span class="hljs-comment">//将得到的班级名发送给观察者</span>
                                        <span class="hljs-keyword">return</span> classInfo.getClassName();
                                    }
                                });
                    }
                }).subscribe(subscriber);</code>

这里用到了两次变换,以此类推,flatMap方法能实现多级数据变换

六、模拟使用场景Demo

以下演示一个网络请求百度lOGO的例子

在MainActivity的onCreate方法

<code>        ImageView baidu = (ImageView) findViewById(R.id.baidu);

        Observable.just(<span class="hljs-string">"https://www.baidu.com/img/bd_logo1.png"</span>)
                .map(<span class="hljs-keyword">new</span> Func1&lt;String, Bitmap&gt;() {
                    <span class="hljs-annotation">@Override</span>
                    <span class="hljs-keyword">public</span> Bitmap <span class="hljs-title">call</span>(String url) {
                        <span class="hljs-comment">//我这里采用okHttp的同步方法下载图片</span>
                        <span class="hljs-keyword">return</span> download(url);
                    }
                }).subscribeOn(Schedulers.io())<span class="hljs-comment">//定义IO线程为事件产生线程</span>
                .observeOn(AndroidSchedulers.mainThread())<span class="hljs-comment">//定义主线程为事件消费线程</span>
                .doOnSubscribe(<span class="hljs-keyword">new</span> Action0() {
                <span class="hljs-comment">//指定运行线程的方法,在订阅发生时启动,用来替换onStart方法</span>
                <span class="hljs-comment">//这里用来在启动时给ImageView设置默认图片</span>
                    <span class="hljs-annotation">@Override</span>
                    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">call</span>() {
                        baidu.setImageResource(R.mipmap.ic_launcher);
                    }
                }).subscribeOn(AndroidSchedulers.mainThread())<span class="hljs-comment">//指定doOnSubscribe方法运行在什么线程</span>
                <span class="hljs-comment">//必须放在定义事件产生线程的后面</span>
                .subscribe(<span class="hljs-keyword">new</span> Subscriber&lt;Bitmap&gt;() {
                    <span class="hljs-annotation">@Override</span>
                    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCompleted</span>() {
                    }

                    <span class="hljs-annotation">@Override</span>
                    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onError</span>(Throwable e) {
                        baidu.setImageResource(R.mipmap.ic_launcher);
                        <span class="hljs-comment">//下载失败显示图标</span>
                    }

                    <span class="hljs-annotation">@Override</span>
                    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onNext</span>(Bitmap bitmap) {
                        baidu.setImageBitmap(bitmap);
                    }
                });
</code>

未经允许不得转载:冰点网络 » RxJava简易上手指南

分享到:更多 ()

评论 抢沙发

评论前必须登录!