RxJava学习(一),RxJava初识

RxJava初识


RxJava是什么

官方介绍:RxJava – Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM.

一个使用可观察队列异步和基于事件开发的库。

RxJava的优势

  1. 链式调用,代码逻辑简洁
  2. 异步切换方便

假设有这样一个需求:界面上有一个自定义的视图 imageCollectorView ,它的作用是显示多张图片,并能使用 addImage(Bitmap) 方法来任意增加显示的图片。现在需要程序将一个给出的目录数组 File[] folders 中每个目录下的 png 图片都加载出来并显示在 imageCollectorView 中。需要注意的是,由于读取图片的这一过程较为耗时,需要放在后台执行,而图片的显示则必须在 UI 线程执行。常用的实现方式有多种,我这里贴出其中一种:

常规方式:

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();

使用RxJava:

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");
        }
    })
    //图片文件转换成Bitmap
    .map(new Func1<File, Bitmap>() {
        @Override
        public Bitmap call(File file) {
            return getBitmapFromFile(file);
        }
    })
    .subscribeOn(Schedulers.io()) // 指定在IO线程做上述操作
    .observeOn(AndroidSchedulers.mainThread())//在主线程显示
    .subscribe(new Action1<Bitmap>() {
        @Override
        public void call(Bitmap bitmap) {
            imageCollectorView.addImage(bitmap);
        }
    });

注意:虽然看起来代码量大了,但此处说的是 逻辑 简洁,当然,如果结合lambda,也能实现代码简洁。
使用RxJava+lambda:

Observable.from(folders)
    .flatMap(f -> Observable.from(f.listFiles()))
    .filter(f -> file.getName().endsWith(".png"))
    .map(f -> getBitmapFromFile(file))
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(b -> imageCollectorView.addImage(bitmap));

因为这个原因,所以网上很多RxJava的介绍都是和lambda结合着写的。不过现在Android Studio也会将代码自动缩进成lambda格式。

RxJava初识和API使用

RxJava依照了LINQ-style:利用泛型实现使用相同API操作不同数据源。

扩展的观察者模式

常规的观察者模式:

场景被观察者观察者订阅事件
常规观察者模式ObservableObserversubscribe/registeronEvent
Android给按钮设置点击监听ButtonOnClickListenersetOnClickListeneronClick
RxJava中ObservableObserversubscribeonNext,onCompleted,onError

RxJava中扩展了事件,增加了onCompleted()onError()onNext()对应事件onEvent(),上文介绍也说了是Observable队列,队列中每一次事件触发会调用onNext(),当所有事件结束,会调用onCompleted()并且队列终止,事件队列过程中发生了异常会调用onError()并且终止队列。

RxJava创建与使用

1. 创建Observer
观察者Observer决定了事件被触发了需要做什么事情

Observer<String> observer = new rx.Observer<String>() {
            @Override
            public void onCompleted() {
                //事件队列执行完成
            }

            @Override
            public void onError(Throwable e) {
                //事件队列执行出现错误
            }

            @Override
            public void onNext(String s) {
                //可多次调用
            }
        };

除了 Observer 接口之外,RxJava 还内置了一个实现了 Observer 的抽象类:Subscriber。 Subscriber 对 Observer 接口进行了一些扩展,但他们的基本使用方式是完全一样的:

      Subscriber<String> subscriber = new Subscriber<String>() {
            @Override
            public void onStart() {
                super.onStart();
            }

            @Override
            public void onCompleted() {
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onNext(String s) {
            }
        };

实际上Observer在subscribe的过程中也会转换成Subscriber,源码如下:

    public final Subscription subscribe(final Observer<? super T> observer) {
        if (observer instanceof Subscriber) {
            return subscribe((Subscriber<? super T>)observer);
        }
        return subscribe(new ObserverSubscriber<T>(observer));
    }

Subscriber相对于Observer的扩展点:增加了onStart(),在事件触发前调用;增加了unsubscribe(),调用后Subscriber不会再接受事件,因为在 subscribe() 之后, Observable 会持有 Subscriber 的引用,这个引用如果不能及时被释放,将有内存泄露的风险。所以最好保持一个原则:要在不再使用的时候尽快在合适的地方(例如 onPause() onStop() 等方法中)调用 unsubscribe() 来解除引用关系,以避免内存泄露的发生;isUnsubscribed()可判断是否已经取消订阅。

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

        Observable<String> observable = Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> subscriber) {
                subscriber.onNext("111");
                subscriber.onNext("222");
                subscriber.onNext("333");
                subscriber.onCompleted();
            }
        });

这里传入了一个 OnSubscribe 对象作为参数,OnSubscribe 会被存储在返回的 Observable 对象中当 Observable 被订阅的时候,OnSubscribe 的 call() 方法会自动被调用。
create() 方法是 RxJava 最基本的创造事件序列的方法。基于这个方法, RxJava 还提供了一些方法用来快捷创建事件队列,例如:

Observable<String> observable = Observable.just("111", "222", "333");
Observable<String> observable = Observable.from(new String[]{"111", "222", "333"});

这两个方式都和上面的方式等同

3. subscribe
创建了 Observable 和 Observer 之后,再用 subscribe() 方法将它们联结起来,整条链子就可以工作了。代码形式很简单:

observable.subscribe(subscriber);
observable.subscribe(observer);

Observable.subscribe(Subscriber) 的内部实现是这样的(仅核心代码):

public Subscription subscribe(Subscriber subscriber) {
    subscriber.onStart();
    onSubscribe.call(subscriber);
    return subscriber;
}

由此可见,Observable并不是在创建的时候就立即发送事件,而是在subscribe()执行的时候开始的。

subscribe() 还支持不完整定义的回调,RxJava 会自动根据定义创建出 Subscriber 。形式如下:

 Action1<String> onNextAction = new Action1<String>() {
            // onNext()
            @Override
            public void call(String s) {
            }
        };
        Action1<Throwable> onErrorAction = new Action1<Throwable>() {
            // onError()
            @Override
            public void call(Throwable throwable) {
            }
        };
        Action0 onCompletedAction = new Action0() {
            // onCompleted()
            @Override
            public void call() {
            }
        };

        Observable<String> observable = Observable.just("111", "222", "333");
        observable.subscribe(onNextAction, onErrorAction, onCompletedAction);

有多个参数的方法:

Subscription subscribe(final Action1<? super T> onNext)
Subscription subscribe(final Action1<? super T> onNext, final Action1<Throwable> onError)
Subscription subscribe(final Action1<? super T> onNext, final Action1<Throwable> onError, final Action0 onCompleted)

变换

变换就是将事件序列中的对象或整个序列进行加工处理,转换成不同的事件或事件序列。
1. 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);
        }
    });

可能会觉得这样写有什么意义呢,直接写showBitmap(getBitmapFromPath())不是一样吗?
还真不一样,第一点,完全按照了一个步骤只做一件事的原则;第二点,也是关键的一点,别忘了,我们显示图片要放到主线程,用RxJava这样写法的话,我可以将这两个拆分的步骤分别放入不同的线程,读取文件转换成Bitmap放入IO线程,显示Bitmap图像放到主线程。具体方式,接下来的scheduler部分讲解。

此处用到了另一个不完整定义的回调,Func1,和ActionX类似,FuncX也有多个,它们的区别是一个有返回值,一个无返回值。FuncX的最后一个泛型类型是返回值得类型。

2. flatMap
比如现在有个查询本地数据库所有城市名的方法

public Observable<List<String>> queryCity();

也就是说我现在得到的是一个城市名的List,而我要做的是打印每个城市名,怎么才能转换成功呢?用map肯定不行了,那加for?

queryCity().subscribe(new Action1<List<String>>() {
            @Override
            public void call(List<String> cityNames) {
                for (String cityName : cityNames) {
                    System.out.println(cityName);
                }
            }
        });

这样确实可以实现,但是就和我们RxJava的理念背道而驰了,这里就需要使用flatmap:

queryCity().flatMap(new Func1<List<String>, Observable<String>>() {
            @Override
            public Observable<String> call(List<String> cityNames) {
                return Observable.from(cityNames);
            }
        }).subscribe(new Action1<String>() {
            @Override
            public void call(String cityName) {
                System.out.println(cityName);
            }
        });

map的Func方法是将一个对象,转换成另一种类型的对象,而flatMap是将一个对象转换成另一个对象的Observable。本来是两个级别,通过flatMap变成一个级别的队列,这就是flat(铺平)的过程。有点类似扁平化管理,原本总经理的一个指令要通过部门主管才能通知到员工,flat过后,直接总经理调度每个员工。

3. lift
lift是所有变换的基础,lift的基本用法:

        Observable.just("1111", "2222").lift(new Observable.Operator<Integer, String>() {
            @Override
            public Subscriber<? super String> call(final Subscriber<? super Integer> subscriber) {
                return new Subscriber<String>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onNext(String s) {
                        subscriber.onNext(Integer.parseInt(s));
                    }
                };
            }
        }).subscribe(new Action1<Integer>() {
            @Override
            public void call(Integer integer) {
                System.out.println(integer);
            }
        });

通过lift把一个Observable转换成了Observable,map能更快捷得达到相同的效果。用是我们自己来控制变换的过程。再来看lift的原理,关键源码如下():

public <R> Observable<R> lift(Operator<? extends R, ? super T> operator) {
    return Observable.create(new OnSubscribe<R>() {
        @Override
        public void call(Subscriber subscriber) {
            Subscriber newSubscriber = operator.call(subscriber);
            newSubscriber.onStart();
            onSubscribe.call(newSubscriber);
        }
    });
}

lift的过程用到了下方列表中的这些元素(我们用 Old 代表原本的类型,New 代表新的类型,方便理解):

老类型的元素新类型的元素
Observable<Old>Observable<New>
OnSubscribe<Old>OnSubscribe<New>
Subscriber<Old>Subscriber<New>

lift生成了一个Observable<New>返回,Observable<New>被subscribe时,会触发OnSubscribe<New>,OnSubscribe<New>的call里调用了OnSubscribe<Old>的call,即触发了Subscriber<Old>,又通过代理机制,在Subscriber<Old>中分别调用了Subscriber<New>对应的方法。

4. compose
一系列Observable操作处理的合并。
如果要合并一系列操作(以下代码只是看逻辑流程,都没写出一些细节的代码)
糟糕的方法:
虽然也能实现,但不符合链式调用模式

transOvservable(Observable.just(1000, 2000)).subscribe(new Action1<String>() {
            @Override
            public void call(String s) {

            }
        });

    public Observable<String> transOvservable(Observable<Integer> observableOrigin) {
        return observableOrigin.subscribeOn(Schedulers.io())
                .observeOn(Schedulers.newThread())
                .lift(new Observable.Operator<String, Integer>() {
                    @Override
                    public Subscriber<? super Integer> call(Subscriber<? super String> subscriber) {
                        return null;
                    }
                }).map(new Func1<String, String>() {
                    @Override
                    public String call(String s) {
                        return null;
                    }
                });
    }

正确的方式

        Observable.Transformer<Integer, String> transformer = new Observable.Transformer<Integer, String>() {
            @Override
            public Observable<String> call(Observable<Integer> integerObservable) {
                return integerObservable.subscribeOn(Schedulers.io())
                        .observeOn(Schedulers.newThread())
                        .lift(new Observable.Operator<String, Integer>() {
                            @Override
                            public Subscriber<? super Integer> call(Subscriber<? super String> subscriber) {
                                return null;
                            }
                        }).map(new Func1<String, String>() {
                            @Override
                            public String call(String s) {
                                return null;
                            }
                        });
            }
        };

        Observable.just(1000, 2000).compose(transformer).subscribe(new Action1<String>() {
            @Override
            public void call(String s) {

            }
        });

scheduler

在不指定线程的情况下, RxJava 遵循的是线程不变的原则,即:在哪个线程调用 subscribe(),就在哪个线程生产事件;在哪个线程生产事件,就在哪个线程消费事件。如果需要切换线程,就需要用到 Scheduler (调度器)。

1. Scheduler的使用

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 主线程运行。

Observable.just(1, 2, 3, 4)
    .subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程
    .observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程
    .subscribe(new Action1<Integer>() {
        @Override
        public void call(Integer number) {
            Log.d(tag, "number:" + number);
        }
    });

上述示例代码中,创建事件内容1,2,3,4将会在IO线程执行,Subscriber的call会在主线程执行。
最开始显示图片的例子和此示例类似。

2. 使用Scheduler多次切换线程

上面例子很好的指定了被订阅的发生线程,和订阅被触发的发生线程,但是实际开发中,肯定会遇到比这复杂得多的逻辑。来看一个复杂的逻辑链(只体现逻辑,一些具体代码被忽略):

Observable.just(1, 2, 3, 4) // IO 线程,由 subscribeOn() 指定
    .subscribeOn(Schedulers.io())
    .observeOn(Schedulers.newThread())
    .map(mapOperator) // 新线程,由 observeOn() 指定
    .observeOn(Schedulers.io())
    .map(mapOperator2) // IO 线程,由 observeOn() 指定
    .observeOn(AndroidSchedulers.mainThread) 
    .subscribe(subscriber);  // Android 主线程,由 observeOn() 指定

通过observeOn的多次调用实现了线程的多次切换,在Scheduler第一个示例代码给人感觉,observeOn() 就是指定 Subscriber 的线程,没错,但这个 Subscriber 并不是(严格说应该为『不一定是』,但这里不妨理解为『不是』)subscribe() 参数中的 Subscriber ,而是 observeOn() 执行时的当前 Observable 所对应的 Subscriber ,即它的直接下级 Subscriber 。换句话说,observeOn() 指定的是它之后的操作所在的线程。因此如果有多次切换线程的需求,只要在每个想要切换线程的位置调用一次 observeOn() 即可。当使用了多个 subscribeOn() 的时候,只有第一个 subscribeOn() 起作用。

3. doOnxxx.

虽然超过一个的 subscribeOn() 对事件处理的流程没有影响,但在流程之前却是可以利用的。

在前面讲 Subscriber 的时候,提到过 Subscriber 的 onStart() 可以用作流程开始前的初始化。然而 onStart() 由于在 subscribe() 发生时就被调用了,因此不能指定线程,而是只能执行在 subscribe() 被调用时的线程。这就导致如果 onStart() 中含有对线程有要求的代码(例如在界面上显示一个 ProgressBar,这必须在主线程执行),将会有线程非法的风险,因为有时你无法预测 subscribe() 将会在什么线程执行。

而与 Subscriber.onStart() 相对应的,有一个方法 Observable.doOnSubscribe() 。它和 Subscriber.onStart() 同样是在 subscribe() 调用后而且在事件发送前执行,但区别在于它可以指定线程。默认情况下, doOnSubscribe() 执行在 subscribe() 发生的线程;而如果在 doOnSubscribe() 之后有 subscribeOn() 的话,它将执行在离它最近的 subscribeOn() 所指定的线程。

同理,还有doOnNext(),doOnComplete()等

主要参考http://gank.io/post/560e15be2dca930e00da1083,记录了一些关键点。和一点自己的理解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值