Android Rxjava 使用

来源:互联网 发布:2016民营经济数据 编辑:程序博客网 时间:2024/05/17 04:05

RxJava

给 Android 开发者的 RxJava 详解

  • 网址 http://gank.io/post/560e15be2dca930e00da1083

RxJava 介绍

  • 异步库
  • 简洁
    • 随着程序逻辑变得越来越复杂,它依然能够保持简洁。
    • 对比 AsyncTask / Handler / XXX / …

观察者模式

  • A 对象(观察者)对 B 对象(被观察者)的某种变化高度敏感,需要在 B 对象(被观察者)变化的一瞬间做出反应。
  • 观察者采用注册(Register)或者称为订阅(Subscribe)的方式,绑定被观察者,使得被观察者的某某状态变化时通知观察者。
  • Android 开发中一个比较典型的例子是点击监听器 OnClickListener (接口)。对设置 OnClickListener 来说, View 是被观察者, OnClickListener 是观察者,二者通过 setOnClickListener() 方法达成订阅关系。订阅之后用户点击按钮的瞬间,Android Framework 就会将点击事件发送给已经注册的 OnClickListener 。
  • 采取这样被动的观察方式,既省去了反复检索状态的资源消耗,也能够得到最高的反馈速度。
  • OnClickListener (接口) 的模式如下图:

  • 如图所示,通过 setOnClickListener() 方法,Button 持有 OnClickListener 的引用;当用户点击时,Button 自动调用 OnClickListener 的 onClick() 方法。
  • 另外,如果把这张图中的概念抽象出来
    • Button -> 被观察者
    • OnClickListener -> 观察者
    • setOnClickListener() -> 订阅
    • onClick() -> 事件
  • 就由专用的观察者模式(只用于监听控件点击)转变成了通用的观察者模式。
  • 如下图 通用观察者模式:

 RxJava 的观察者模式

  • 由被观察者调用了观察者的回调方法,就实现了由被观察者向观察者的事件传递。
  • RxJava 作为一个工具库,使用的是通用形式观察者模式。
  • 基本定义
    • Observable (可观察者,即被观察者)
    • Observer (观察者)
    • subscribe (订阅)
    • 事件
    • Observable (被观察者) 和 Observer (观察者) 通过 subscribe() (订阅)方法实现订阅关系,从而 Observable (被观察者) 可以在需要的时候发出事件来通知 Observer (观察者) 。
    • RxJava 的事件回调方法除了普通事件 onNext() (相当于 onClick() / onEvent())之外,还定义了两个特殊的事件:onCompleted() 和 onError()。
      • onCompleted(): 事件队列完结。
        • RxJava 不仅把每个事件单独处理,还会把它们看做一个队列。
        • RxJava 规定,当不会再有新的 onNext() 发出时,需要触发 onCompleted() 方法作为标志。
      • onError(): 事件队列异常。
        • 在事件处理过程中出异常时,onError() 会被触发,同时队列自动终止,不允许再有事件发出。
      • 在一个正确运行的事件序列中, onCompleted() 和 onError() 有且只有一个,并且是事件序列中的最后一个。
        • 需要注意的是,onCompleted() 和 onError() 二者也是互斥的,即在队列中调用了其中一个,就不应该再调用另一个。
  • RxJava 的观察者模式如下图:

RxJava 的基本实现

  • RxJava 的基本实现主要有三点:

    • 创建 Observer (观察者)

      • Observer 即观察者,它决定事件触发的时候将有怎样的行为。
      • RxJava 中的 Observer 接口的实现方式:

        Observer<String> observer = new Observer<String>() {    @Override    public void onNext(String s) {        Log.d(tag, "Item: " + s);    }    @Override    public void onCompleted() {        Log.d(tag, "Completed!");    }    @Override    public void onError(Throwable e) {        Log.d(tag, "Error!");    }};
      • 除了 Observer 接口之外,RxJava 还内置了一个实现了 Observer 的抽象类:Subscriber。 Subscriber 对 Observer 接口进行了一些扩展,但他们的基本使用方式是完全一样的:

        Subscriber<String> subscriber = new Subscriber<String>() {    @Override    public void onNext(String s) {        Log.d(tag, "Item: " + s);    }    @Override    public void onCompleted() {        Log.d(tag, "Completed!");    }    @Override    public void onError(Throwable e) {        Log.d(tag, "Error!");    }};
      • 在 RxJava 的 subscribe (订阅) 过程中,Observer 也总是会先被转换成一个 Subscriber 再使用。所以在使用基本功能时,选择 Observer 和 Subscriber 是完全一样的。
      • 区别:
        • Subscriber 的 onStart():
          • 这是 Subscriber 增加的方法。
          • 它会在 subscribe 刚开始,而事件还未发送之前被调用,可以用于做一些准备工作,例如数据的清零或重置。
          • 这是一个可选方法,默认情况下它的实现为空。
          • 需要注意的是,如果对准备工作的线程有要求(例如弹出一个显示进度的对话框,这必须在主线程执行), onStart() 就不适用了,因为它总是在 subscribe() 所发生的线程被调用,而不能指定线程。* 要在指定的线程来做准备工作,可以使用 doOnSubscribe() 方法。
        • Subscriber 的 unsubscribe():
          • 这是 Subscriber 所实现的另一个接口 Subscription 的方法,用于取消订阅。
          • 在这个方法被调用后,Subscriber 将不再接收事件。一般在这个方法调用前,可以使用 isUnsubscribed() 先判断一下状态。
          • unsubscribe() 这个方法很重要,因为在 subscribe() 之后, Observable 会持有 Subscriber 的引用,这个引用如果不能及时被释放,将有内存泄露的风险。
          • 所以最好保持一个原则:要在不再使用的时候尽快在合适的地方(例如 onPause() / onStop() 等方法中)调用 unsubscribe() 来解除引用关系,以避免内存泄露的发生。
    • 创建 Observable (被观察者)

      • Observable 即被观察者,它决定什么时候触发事件以及触发怎样的事件。
      • RxJava 使用 create() 方法来创建一个 Observable ,并为它定义事件触发规则:

        Observable observable = Observable.create(new Observable.OnSubscribe<String>() {    @Override    public void call(Subscriber<? super String> subscriber) {        subscriber.onNext("Hello");        subscriber.onNext("Hi");        subscriber.onNext("Aloha");        subscriber.onCompleted();    }});
      • 传入了一个 OnSubscribe() 对象作为参数。并且 OnSubscribe() 对象会被存储在返回的 Observable 对象中。
        • OnSubscribe() 对象的作用相当于一个计划表
        • 当 Observable 被订阅的时候,OnSubscribe() 对象的 call() 方法会自动被调用,事件序列就会依照设定依次触发
        • 对于上面的代码,就是观察者Subscriber 将会被调用三次 onNext() 和一次 onCompleted()
        • 由被观察者调用了观察者的回调方法,就实现了由被观察者向观察者的事件传递,即RxJava的观察者模式。
      • 总结
        • 这个例子很简单:事件的内容是字符串,而不是一些复杂的对象;
        • 事件的内容是已经定好了的,而不像有的观察者模式一样是待确定的(例如网络请求的结果在请求返回之前是未知的);
        • 所有事件在一瞬间被全部发送出去,而不是夹杂一些确定或不确定的时间间隔或者经过某种触发器来触发的。
        • 虽然简单但便于说明,
        • 实质上只要,各种各样的事件发送规则都可以自己来写。
      • create() 方法是 RxJava 最基本的创造事件序列的方法。基于这个方法, RxJava 还提供了一些和create()等价的方法用来快速创建事件队列,例如:

        • just(T…): 将传入的参数依次发送出来。

          Observable observable = Observable.just("Hello", "Hi", "Aloha");// 将会依次调用:// onNext("Hello");// onNext("Hi");// onNext("Aloha");// onCompleted();
        • from(T[]) / from(Iterable

Scheduler (调度器) 线程控制

  • 在不指定线程的情况下, RxJava 遵循的是线程不变的原则,即:在哪个线程调用 subscribe(),就在哪个线程生产事件;在哪个线程生产事件,就在哪个线程消费事件。如果需要切换线程,就需要用到 Scheduler (调度器)。
  • 观察者模式本身的目的就是『后台处理,前台回调』的异步机制,因此异步对于 RxJava 是至关重要的。

Scheduler 的 API (一)

  • 在RxJava 中,Scheduler ——调度器,相当于线程控制器,RxJava 通过它来指定每一段代码应该运行在什么样的线程。
  • RxJava 已经内置了几个 Scheduler ,它们已经适合大多数的使用场景:
    • Schedulers.immediate(): 直接在当前线程运行,相当于不指定线程。这是默认的 Scheduler。
    • Schedulers.newThread(): 总是启用新线程,并在新线程执行操作。
    • Schedulers.io(): I/O 操作(读写文件、读写数据库、网络信息交互等)所使用的 Scheduler。
      • 行为模式和 newThread() 差不多
      • 区别在于 io() 的内部实现是用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下 io() 比 newThread() 更有效率。
      • 不要把计算工作放在 io() 中,以避免创建不必要的线程。
    • Schedulers.computation(): 计算所使用的 Scheduler。
      • 这个计算指的是 CPU 密集型计算,即不会被 I/O 等操作限制性能的操作,例如图形的计算。
      • 这个 Scheduler 使用的固定的线程池,大小为 CPU 核数。
      • 不要把 I/O 操作放在 computation() 中,否则 I/O 操作的等待时间会浪费 CPU。
    • Android 还有一个专用的 AndroidSchedulers.mainThread(),它指定的操作将在 Android 主线程运行。
  • 使用 subscribeOn() 和 observeOn() 两个方法来对线程进行控制了。
    • subscribeOn(): 指定 subscribe() 所发生的线程,即 Observable.OnSubscribe 被激活时所处的线程。或者叫做事件产生的线程。
    • observeOn(): 指定 Subscriber 所运行在的线程。或者叫做事件消费的线程。
  • 示例:

    Observable.just(1, 2, 3, 4).subscribeOn(Schedulers.io()) // 指定 subscribe()方法(Observable.OnSubscribe()方法),发生在 IO 线程.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程.subscribe(new Action1<Integer>() {    @Override    public void call(Integer number) {        Log.d(tag, "number:" + number);    }});
  • 总结:
    • 上面这段代码中,由于 subscribeOn(Schedulers.io()) 的指定,被创建的事件的内容 1、2、3、4 将会在 IO 线程发出;
    • 而由于 observeOn(AndroidScheculers.mainThread()) 的指定,因此 subscriber 数字的打印将发生在主线程 。
    • 事实上,这种在 subscribe() 之前写上两句 subscribeOn(Scheduler.io()) 和 observeOn(AndroidSchedulers.mainThread()) 的使用方式非常常见,它适用于多数的 『后台线程取数据,主线程显示』的程序策略。
  • 由 id 取得图片并显示 修改

    int drawableRes = ...;ImageView imageView = ...;Observable.create(new OnSubscribe<Drawable>() {    @Override    public void call(Subscriber<? super Drawable> subscriber) {        Drawable drawable = getTheme().getDrawable(drawableRes));        subscriber.onNext(drawable);        subscriber.onCompleted();    }}).subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程.subscribe(new Observer<Drawable>() {    @Override    public void onNext(Drawable drawable) {        imageView.setImageDrawable(drawable);    }    @Override    public void onCompleted() {    }    @Override    public void onError(Throwable e) {        Toast.makeText(activity, "Error!", Toast.LENGTH_SHORT).show();    }});

Scheduler 的 API (二)

  • subscribeOn() 和 observeOn() 的内部实现,也是用的 lift()。
  • 具体看图(不同颜色的箭头表示不同的线程):
  • subscribeOn() 原理图:

  • observeOn() 原理图:

    • 从图中可以看出,subscribeOn() 和 observeOn() 都做了线程切换的工作(图中的 “schedule…” 部位)。
      • 不同的是, subscribeOn() 的线程切换发生在 OnSubscribe 中,即在它通知上一级 OnSubscribe 时,这时事件还没有开始发送,因此 subscribeOn() 的线程控制可以从事件发出的开端就造成影响;
      • 而 observeOn() 的线程切换则发生在它内建的 Subscriber 中,即发生在它即将给下一级 Subscriber 发送事件时,因此 observeOn() 控制的是它后面的线程。
  • 当多个 subscribeOn() 和 observeOn() 混合使用时,线程调度是怎么发生的(由于图中对象较多,相对于上面的图对结构做了一些简化调整):

    • 图中共有 5 处含有对事件的操作。
      • ①和②两处受第一个 subscribeOn() 影响,运行在红色线程;
      • ③和④处受第一个 observeOn() 的影响,运行在绿色线程;
      • ⑤处受第二个 onserveOn() 影响,运行在紫色线程;
      • 而第二个 subscribeOn() ,由于在通知过程中线程就被第一个 subscribeOn() 截断,因此对整个流程并没有任何影响。这里也就回答了前面的问题:当使用了多个 subscribeOn() 的时候,只有第一个 subscribeOn() 起作用。

延伸:doOnSubscribe()

  • 虽然超过一个的 subscribeOn() 对事件处理的流程没有影响,但在流程之前却是可以利用的。
  • Subscriber 的 onStart() 可以用作流程开始前的初始化。然而 onStart() 由于在 subscribe() 发生时就被调用了,因此不能指定线程,而是只能执行在 subscribe() 被调用时的线程。
  • 导致如果 onStart() 中含有对线程有要求的代码(例如在界面上显示一个 ProgressBar,这必须在主线程执行),将会有线程非法的风险,因为有时你无法预测 subscribe() 将会在什么线程执行。
  • 而与 Subscriber.onStart() 相对应的,有一个方法 Observable.doOnSubscribe() 。它和 Subscriber.onStart() 同样是在 subscribe() 调用后而且在事件发送前执行。
  • 区别在于它可以指定线程。默认情况下, doOnSubscribe() 执行在 subscribe() 发生的线程;
  • 而如果在 doOnSubscribe() 之后有 subscribeOn() 的话,它将执行在离doOnSubscribe()最近的 subscribeOn() 所指定的线程。
  • 示例:

    Observable.create(onSubscribe)    .subscribeOn(Schedulers.io()) // IO 线程    .doOnSubscribe(new Action0() {        @Override        public void call() {            progressBar.setVisibility(View.VISIBLE); // 需要在主线程执行        }    })    .subscribeOn(AndroidSchedulers.mainThread()) // 指定主线程 ( doOnSubscribe()之后,最近的 subscribeOn() )    .observeOn(AndroidSchedulers.mainThread())    .subscribe(subscriber);

变换 (Rxjava 操作符)

  • RxJava 提供了对事件序列进行变换的支持,这是它的核心功能之一。所谓变换,就是将事件序列中的对象或整个序列进行加工处理,转换成不同的事件或事件序列。

map() 操作符

    Observable.just("images/logo.png") // 输入类型 String            .map(new Func1<String, Bitmap>() {                @Override                public Bitmap call(String filePath) { // 参数类型 String                    return getBitmapFromPath(filePath); // 返回类型 Bitmap                }            })            .subscribe(new Action1<Bitmap>() {                @Override                public void call(Bitmap bitmap) { // 参数类型 Bitmap                    showBitmap(bitmap);                }            });
  • Func1 类

    • Func1 类和 Action1 相似,是 RxJava 的一个接口,用于包装含有一个参数的方法。
    • Func1 和 Action 的区别在于, Func1 包装的是有返回值的方法。
    • 另外,和 ActionX 一样, FuncX 也有多个,用于不同参数个数的方法。
    • FuncX 和 ActionX 的区别在 FuncX 包装的是有返回值的方法。
  • 总结:

    • map() 方法将参数中的 String 对象转换成一个 Bitmap 对象后返回。
    • 而在经过 map() 方法后,事件的参数类型由 String 转为了 Bitmap。
    • 这种直接变换对象并返回的操作,是最常见的也最容易理解的变换。
    • 不过 RxJava 的变换远不止这样,它不仅可以针对事件对象,还可以针对整个事件队列
  • map(): 事件对象的直接变换。 map() 的示意图:

flatMap() 操作符

  • 示例:
  • 首先假设这么一种需求:假设有一个数据结构『学生』,现在需要打印出一组学生的名字。

    Student[] students = ...;Subscriber<String> subscriber = new Subscriber<String>() {    @Override    public void onNext(String name) {        Log.d(tag, name);    }    ...};Observable.from(students)    .map(new Func1<Student, String>() {        @Override        public String call(Student student) {            return student.getName();        }    })    .subscribe(subscriber);
  • 那么再假设:如果要打印出每个学生所需要修的所有课程的名称呢?(需求的区别在于,每个学生只有一个名字,但却有多个课程。)首先可以这样实现:

    Student[] students = ...;Subscriber<Student> subscriber = new Subscriber<Student>() {    @Override    public void onNext(Student student) {        List<Course> courses = student.getCourses();        for (int i = 0; i < courses.size(); i++) {            Course course = courses.get(i);            Log.d(tag, course.getName());        }    }    ...};Observable.from(students)    .subscribe(subscriber);
  • 如果不想在 Subscriber 中使用 for 循环,而是希望 Subscriber 中直接传入单个的 Course 对象呢(这对于代码复用很重要)?用 map() 显然是不行的,因为 map() 是一对一的转化,而我现在的要求是一对多的转化。那怎么才能把一个 Student 转化成多个 Course 呢?

    Student[] students = ...;Subscriber<Course> subscriber = new Subscriber<Course>() {    @Override    public void onNext(Course course) {        Log.d(tag, course.getName());    }    ...};Observable.from(students) // 循环    .flatMap(new Func1<Student, Observable<Course>>() {        @Override        public Observable<Course> call(Student student) {            return Observable.from(student.getCourses()); // 循环        }    })    .subscribe(subscriber);
  • 总结:
    • flatMap() 和 map() 有一个相同点:
      • flatMap()也是把传入的参数转化之后返回另一个对象。
      • 但需要注意,和 map() 不同的是, flatMap() 中返回的是个 Observable 对象,
      • 并且这个 Observable 对象并不是被直接发送到了 Subscriber 的回调方法中。
    • flatMap() 的原理:
      • 使用传入的事件对象创建一个 Observable 对象;
      • 并不发送这个 Observable, 而是将它激活,于是它开始发送事件;
      • 每一个创建出来的 Observable 发送的事件,都被汇入同一个 Observable ,而这个 Observable 负责将这些事件统一交给 Subscriber 的回调方法。
      • 这三个步骤,把事件拆成了两级,通过一组新创建的 Observable 将初始的对象『铺平』之后通过统一路径分发了下去。而这个『铺平』就是 flatMap() 所谓的 flat。
  • flatMap() 示意图:

变换原理 lift()

  • 这些变换虽然功能各有不同,但实质上都是针对事件序列的处理和再发送。
  • 而在 RxJava 的内部,其他变换操作符是基于同一个基础的变换方法: lift(Operator)。
  • RxJava 都不建议开发者自定义 Operator 来直接使用 lift(),而是建议尽量使用已有的 lift() 包装方法(如 map() / flatMap() 等)进行组合来实现需求,因为直接使用 lift() 非常容易发生一些难以发现的错误。
  • lift() 的内部实现(仅核心代码):

    // 注意:这不是 lift() 的源码,而是将源码中与性能、兼容性、扩展性有关的代码剔除后的核心代码。// 如果需要看源码,可以去 RxJava 的 GitHub 仓库下载。public <R> Observable<R> lift(Operator<? extends R, ? super T> operator) {    return Observable.create(new OnSubscribe<R>() { // 生成新的 Observable 并返回        @Override        public void call(Subscriber subscriber) { // 触发 新 OnSubscribe 的 call() 方法 -> 旧的 subscriber            Subscriber newSubscriber = operator.call(subscriber);            newSubscriber.onStart();            onSubscribe.call(newSubscriber);// 旧的Observable onSubscribe.call(newSubscriber)        }    });}
    • 在lift()方法中会生成了一个新的 Observable 并返回
    • 虽然创建新 Observable 所用的参数 OnSubscribe 的回调方法 call() 中的实现看起来和 Observable.subscribe() 一样
    • 但其指代的对象不同,onSubscribe.call(newSubscriber) 的 onSubscribe 所指代的对象不同
    • 区别:
      • Observable.subscribe() 中这句话的 onSubscribe 指的是 Observable 中的 onSubscribe 对象
      • 但是,当含有 lift() 时:
        • lift() 会创建了一个新的 Observable,加上之前的原始 Observable,已经有两个 Observable 了;
        • 而同样地,新 Observable 里的新 OnSubscribe 加上之前的原始 Observable 中的原始 OnSubscribe,也就有了两个 OnSubscribe;
        • 当用户调用经过 lift() 后的 Observable 的 subscribe() 的时候,使用的是 lift() 所返回的新的 Observable ,于是新的 Observable所触发的 onSubscribe.call(subscriber),也是用的新 Observable 中的新 OnSubscribe,即在 lift() 中生成的那个 OnSubscribe;
        • 而这个新 OnSubscribe 的 call() 方法中的 onSubscribe.call() ,指的是原始 Observable 中的原始 OnSubscribe ,在这个 call() 方法里的newSubscriber参数是, 新 OnSubscribe 利用 operator.call(subscriber) 生成了一个新的 Subscriber(Operator 就是在这里,通过自己的 call() 方法将新 Subscriber 和原始 Subscriber 进行关联,并插入自己的『变换』代码以实现变换),然后利用这个新 Subscriber 向原始 Observable 进行订阅。
    • lift() 的实现过程,有点像一种代理机制,通过事件拦截和事件处理,实现事件序列的变换。

  • 两次和多次的 lift() 同理,如下图:

  • 总结:
    • 在 Observable 执行了 lift(Operator) 方法之后,会返回一个新的 Observable,这个新的 Observable 会像一个代理一样,负责接收原始的 Observable 发出的事件,并在处理后发送给 Subscriber。
  • 案例:

      observable.lift(new Observable.Operator<String, Integer>() {    @Override    public Subscriber<? super Integer> call(final Subscriber<? super String> subscriber) {// 旧的 subscriber        // 将事件序列中的 Integer 对象转换为 String 对象        return new Subscriber<Integer>() { //新的 Subscriber            @Override            public void onNext(Integer integer) {                subscriber.onNext("" + integer);            }            @Override            public void onCompleted() {                subscriber.onCompleted();            }            @Override            public void onError(Throwable e) {                subscriber.onError(e);            }        };    }});

compose: 对 Observable 整体的变换

  • 除了 lift() 之外, Observable 还有一个变换方法叫做 compose(Transformer)。
  • compose() 和 lift() 的区别在于

    • lift() 是针对事件项和事件序列的
    • compose() 是针对 Observable 自身进行变换
    • 假设在程序中有多个 Observable ,并且他们都需要应用一组相同的 lift() 变换

      observable1    .lift1()    .lift2()    .lift3()    .lift4()    .subscribe(subscriber1);observable2    .lift1()    .lift2()    .lift3()    .lift4()    .subscribe(subscriber2);observable3    .lift1()    .lift2()    .lift3()    .lift4()    .subscribe(subscriber3);observable4    .lift1()    .lift2()    .lift3()    .lift4()    .subscribe(subscriber1);
    • 提高可读性、可维护性 -> liftAll()

      private Observable liftAll(Observable observable) {    return observable        .lift1()        .lift2()        .lift3()        .lift4();}...liftAll(observable1).subscribe(subscriber1);liftAll(observable2).subscribe(subscriber2);liftAll(observable3).subscribe(subscriber3);liftAll(observable4).subscribe(subscriber4);
    • 可读性、可维护性都提高了。可是 Observable 被一个方法包起来,这种方式对于 Observale 的灵活性似乎还是增添了那么点限制。

    • compose() 操作符

      public class LiftAllTransformer implements Observable.Transformer<Integer, String> {    @Override    public Observable<String> call(Observable<Integer> observable) {        return observable            .lift1()            .lift2()            .lift3()            .lift4();    }}...Transformer liftAll = new LiftAllTransformer();observable1.compose(liftAll).subscribe(subscriber1);observable2.compose(liftAll).subscribe(subscriber2);observable3.compose(liftAll).subscribe(subscriber3);observable4.compose(liftAll).subscribe(subscriber4);
    • 使用 compose() 方法,Observable 可以利用传入的 Transformer 对象的 call 方法直接对自身进行处理,也就不必被包在方法的里面了。

Rxjava 综合案例

//子线程new Thread() {    @Override    public void run() {        super.run();        //循环        for (File folder : folders) {            File[] files = folder.listFiles();            //循环            for (File file : files) {                //判断                if (file.getName().endsWith(".png")) {                    //制作                    final Bitmap bitmap = getBitmapFromFile(file);                    //主线程显示                    getActivity().runOnUiThread(new Runnable() {                        @Override                        public void run() {                            imageCollectorView.addImage(bitmap);                        }                    });                }            }        }    }}.start(); Observable    .from(folders) // 循环    .flatMap(new Func1<File, Observable<File>>() {        @Override        public Observable<File> call(File file) {            return Observable.from(file.listFiles()); // 循环        }    })    // 过滤->判断    .filter(new Func1<File, Boolean>() {        @Override        public Boolean call(File file) {            return file.getName().endsWith(".png");        }    })    // 制作    .map(new Func1<File, Bitmap>() {        @Override        public Bitmap call(File file) {            return getBitmapFromFile(file);        }    })    //io 线程    .subscribeOn(Schedulers.io())    //主线程显示    .observeOn(AndroidSchedulers.mainThread())    .subscribe(new Action1<Bitmap>() {        @Override        public void call(Bitmap bitmap) {            imageCollectorView.addImage(bitmap);        }});// Lambda 化Observable.from(folders).flatMap((Func1) (folder) -> { Observable.from(file.listFiles()) }).filter((Func1) (file) -> { file.getName().endsWith(".png") }).map((Func1) (file) -> { getBitmapFromFile(file) }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe((Action1) (bitmap) -> { imageCollectorView.addImage(bitmap) });

Retrofit + Rxjava 拓展案例

  • Retrofit 除了提供了传统的 Callback 形式的 API,还有 RxJava 版本的 Observable 形式 API。
  • 对比 Retrofit 的 RxJava 版 API 和传统版本的区别。

    • 以获取一个 User 对象的接口作为例子。使用Retrofit 的传统 API,你可以用这样的方式来定义请求:

      @GET("/user")public void getUser(@Query("userId") String userId, Callback<User> callback);// 在程序的构建过程中, Retrofit 会把自动把方法实现并生成代码,// 然后开发者就可以利用下面的方法来获取特定用户并处理响应:getUser(userId, new Callback<User>() {    @Override    public void success(User user) {        userView.setUser(user);    }    @Override    public void failure(RetrofitError error) {        // Error handling        ...    }};
    • 使用 RxJava 形式的 API,定义同样的请求:

      @GET("/user")public Observable<User> getUser(@Query("userId") String userId);// 在程序的构建过程中, Retrofit 会把自动把方法实现并生成代码,// 然后开发者就可以利用下面的方法来获取特定用户并处理响应:getUser(userId)    .observeOn(AndroidSchedulers.mainThread())    .subscribe(new Observer<User>() {        @Override        public void onNext(User user) {            userView.setUser(user);        }        @Override        public void onCompleted() {        }        @Override        public void onError(Throwable error) {            // Error handling            ...        }    });
    • 当 RxJava 形式的时候,Retrofit 把请求封装进 Observable ,在请求结束后调用 onNext() 或在请求失败后调用 onError()。

      • Callback 形式和 Observable 形式长得不太一样,但本质都差不多,而且在细节上 Observable 形式似乎还比 Callback 形式要差点。
      • 旦情景复杂起来, Callback 形式马上就会开始让人头疼。比如:
      • 假设这么一种情况:程序取到的 User 并不应该直接显示,而是需要先与数据库中的数据进行比对和修正后再显示。使用 Callback 方式大概可以这么写:

        getUser(userId, new Callback<User>() {    @Override    public void success(User user) {        processUser(user); // 尝试修正 User 数据        userView.setUser(user);    }    @Override    public void failure(RetrofitError error) {        // Error handling        ...    }};
      • 这样做会影响性能。数据库的操作很重,一次读写操作花费 10~20ms 是很常见的,这样的耗时很容易造成界面的卡顿。
      • 所以通常情况下,如果可以的话一定要避免在主线程中处理数据库。所以为了提升性能,这段代码可以优化一下:

        getUser(userId, new Callback<User>() {    @Override    public void success(User user) {        new Thread() {            @Override            public void run() {                processUser(user); // 尝试修正 User 数据                runOnUiThread(new Runnable() { // 切回 UI 线程                    @Override                    public void run() {                        userView.setUser(user);                    }                });            }).start();    }    @Override    public void failure(RetrofitError error) {        // Error handling        ...    }};
      • 杂乱的代码往往不仅仅是美观问题,因为代码越乱往往就越难读懂,而如果项目中充斥着杂乱的代码,无疑会降低代码的可读性,造成团队开发效率的降低和出错率的升高。

      • 如果用 RxJava 的形式,就好办多了。 RxJava 形式的代码是这样的:

        getUser(userId)    // doOnNext()的执行在onNext()之前,对数据进行相关处理。    .doOnNext(new Action1<User>() {        @Override        public void call(User user) {            processUser(user);        })    .observeOn(AndroidSchedulers.mainThread())    .subscribe(new Observer<User>() {        @Override        public void onNext(User user) {            userView.setUser(user);        }        @Override        public void onCompleted() {        }        @Override        public void onError(Throwable error) {            // Error handling            ...        }    });
  • 假设 /user 接口并不能直接访问,而需要填入一个在线获取的 token ,代码应该怎么写?

    • Callback 方式,可以使用嵌套的 Callback:

      @GET("/token")public void getToken(Callback<String> callback);@GET("/user")public void getUser(@Query("token") String token, @Query("userId") String userId, Callback<User> callback);...getToken(new Callback<String>() {    @Override    public void success(String token) {        getUser(token, userId, new Callback<User>() {            @Override            public void success(User user) {                userView.setUser(user);            }            @Override            public void failure(RetrofitError error) {                // Error handling                ...            }        };    }    @Override    public void failure(RetrofitError error) {        // Error handling        ...    }});
    • 使用 RxJava 方式:

      @GET("/token")public Observable<String> getToken();@GET("/user")public Observable<User> getUser(@Query("token") String token, @Query("userId") String userId);...// 返回 Observable<String>// 在订阅时请求 token// 并在响应后发送 tokengetToken() // Retrofit 请求方法    .flatMap(new Func1<String, Observable<User>>() {        @Override        public Observable<User> onNext(String token) {            // 返回 Observable<User>            // 在订阅时请求消息列表            // 并在响应后发送请求到的消息列表            return getUser(token, userId);        })    .observeOn(AndroidSchedulers.mainThread())    .subscribe(new Observer<User>() {        @Override        public void onNext(User user) {            // 处理显示user            userView.setUser(user);        }        @Override        public void onCompleted() {        }        @Override        public void onError(Throwable error) {            // Error handling            ...        }    });
    • 总结:

      • 传统的嵌套请求需要使用嵌套的 Callback 来实现。
      • 而通过 flatMap() ,可以把嵌套的请求写在一条链中,从而保持程序逻辑的清晰。

RxBinding

  • RxBinding 是 Jake Wharton 的一个开源库,它提供了一套在 Android 平台上的基于 RxJava 的 Binding API。
  • 所谓 Binding,就是类似设置 OnClickListener 、设置 TextWatcher 这样的注册绑定对象的 API。
  • 使用 RxBinding ,可以把事件监听用这样的方法来设置:

    Button button = ...;RxView.clickEvents(button) // 以 Observable 形式来反馈点击事件    .subscribe(new Action1<ViewClickEvent>() {        @Override        public void call(ViewClickEvent event) {            // Click handling        }    });
    • 看起来除了形式变了没什么区别,实质上也是这样。甚至如果你看一下它的源码,你会发现它连实现都没什么惊喜:
      • 它的内部是直接用一个包裹着的 setOnClickListener() 来实现的。
      • 然而,仅仅这一个形式的改变,却恰好就是 RxBinding 的目的:扩展性。
      • 通过 RxBinding 把点击监听转换成 Observable 之后,就有了对它进行扩展的可能。
      • 扩展的方式有很多,根据需求而定。
  • throttleFirst(): 在每次事件触发后的一定时间间隔内丢弃新的事件。

    • 常用作去抖动过滤,消除手抖导致的快速连环点击,例如按钮的点击监听器:

      RxView.clickEvents(button) // RxBinding 代码    .throttleFirst(500, TimeUnit.MILLISECONDS) // 设置防抖间隔为 500ms     .subscribe(subscriber);

RxBus

  • RxBus 名字看起来像一个库,但它并不是一个库,而是一种模式,它的思想是使用 RxJava 来实现了 EventBus ,而让你不再需要使用 Otto 或者 GreenRobot 的 EventBus。
0 0