ReactiveX 是一个专注于异步编程与控制可观察数据(或者事件)流的API。
它组合了观察者模式,迭代器模式和函数式编程的优秀思想。
高效、干净和可扩展的方式来处理实时数据处理的这一场景。
RxJava 是 ReactiveX 在 Java 上的开源的实现。
RxJava 最核心的两个参与者 Observables(被观察者、事件源)和Subscribers(观察者)
Observables 发出一系列事件。Subscribers处理这些事件
RxJava 和 观察者模式有一点明显不同,就是如果一个Observable没有任何Subscriber,那么这个Observable不会发出任何事件
RJ 基础写法
订阅建立:myObservable.subscribe(mySubscriber);
Observalbe 创建简写
Observable<String> myObservable = Observable.just("Hello, world!");
只关心Subscriber 的 onNext;方法专属化写法
myObservable.subscribe(onNextAction, onErrorAction, onCompleteAction);
我们不关心onError和onComplete
所以订阅可以写成 mObservable.subscrible(onNextAction);
1.修改打印出的内容
2.对内容进行过滤
3.不在onNext中做变动(真正的响应式)
这里就需要引进操作符了Operators进行变换操作
(摘,写的很好)
因为我希望我的Subscribers越轻量越好,
因为我有可能会在mainThread中运行subscriber。
另外,根据响应式函数编程的概念,Subscribers更应该做的事情是“响应”,
响应Observable发出的事件,而不是去修改。
如果我能在某些中间步骤中对“Hello World!”进行变换是不是很酷?
(摘,写的很好)
操作符 Operator 对 Observable进行变换
之一
(摘,写的很好)
不服?
是不是觉得我们的例子太简单,不足以说服你?你需要明白下面的两点:
1.Observable和Subscriber可以做任何事情
Observable可以是一个数据库查询,Subscriber用来显示查询结果;
Observable可以是屏幕上的点击事件,Subscriber用来响应点击事件;
Observable可以是一个网络请求,Subscriber用来显示请求结果。
2.Observable和Subscriber是独立于中间的变换过程的。
在Observable和Subscriber中间可以增减任何数量的map。
整个系统是高度可组合的,操作数据是一个很简单的过程。
(摘,写的很好)
from
Observable.from()方法,它接收一个集合作为输入,然后每次输出一个元素给subscriber
map()是一对一的转化,而我现在的要求是一对多的转化。那怎么才能把一个 Student 转化成多个 Course 呢?
这个时候,就需要用 flatMap() 了
flatMap
Observable.flatMap()接收一个Observable的输出作为输入,同时输出另外一个Observable。
flatMap输出的新的Observable正是我们在Subscriber想要接收的。
现在Subscriber不再收到List<String>,而是收到一些列单个的字符串,就像Observable.from()的输出一样。
·filter()输出和输入相同的元素,并且会过滤掉那些不满足检查条件的。
·take()输出最多指定数量的结果。
.takeLast(N)只发射最后N个元素
.first()只发射第一个
.last()只发射最后一个
.skip(N)不发射前N个元素而是发射它后面的那些数据的序列
.skipLast()
.distinct 仅处理一次,可以处理去除重复的数据
.elementAt(N)仅从一个序列中发射第n个元素然后就完成了,这里是从0开始计的。
.sample(1000, TimeUnit.MILLISECONDS)定期发射Observable最近发射的数据项
·doOnNext()允许我们在每次输出一个元素之前做一些额外的事情,比如存库
在每次事件触发后的一定时间间隔内丢弃新的事件。
常用作去抖动过滤,例如按钮的点击监听器:
RxView.clickEvents(button) // RxBinding 代码,后面的文章有解释
.throttleFirst(500, TimeUnit.MILLISECONDS) // 设置防抖间隔为 500ms
.subscribe(subscriber);
另外还有merge操作符
以及更多操作符,还可以自定义操作符
操作符 zip
合并两个或者多个Observables发射出的数据项,
根据指定的函数Func2变换它们,并发射一个新值。
还有join startWith 对数据序列进行插入操作
错误处理
这种模式有以下几个优点:
1.只要有异常发生onError()一定会被调用
这极大的简化了错误处理。只需要在一个地方处理错误即可以。
2.操作符不需要处理异常
将异常处理交给订阅者来做,Observerable的操作符调用链中一旦有一个抛出了异常,就会直接执行onError()方法。
3.你能够知道什么时候订阅者已经接收了全部的数据。
使用RxJava,Observable对象根本不需要知道如何处理错误!
操作符也不需要处理错误状态-一旦发生错误,就会跳过当前和后续的操作符。
所有的错误处理都交给订阅者来做。
调度器Scheduler
使用RxJava,你可以使用subscribeOn()指定观察者代码运行的线程,
使用observerOn()指定订阅者运行的线程
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 主线程运行。
有了这几个 Scheduler ,就可以使用 subscribeOn() 和 observeOn() 两个方法来对线程进行控制了。
* subscribeOn(): 事件产生的线程。
* observeOn(): 事件消费的线程。
Subscriber.onStart() 相对应的,有一个方法 Observable.doOnSubscribe() 。
它和 Subscriber.onStart() 同样是在 subscribe() 调用后而且在事件发送前执行,但区别在于它可以指定线程。
默认情况下,doOnSubscribe() 执行在 subscribe() 发生的线程;而如果在 doOnSubscribe() 之后有 subscribeOn() 的话,它将执行在离它最近的 subscribeOn() 所指定的线程。
这个对象代表了被观察者和订阅者之间的联系。
subscription.unsubscribe();
RxJava的另外一个好处就是它处理unsubscribing的时候,会停止整个调用链。
如果你使用了一串很复杂的操作符,调用unsubscribe将会在他当前执行的地方终止。不需要做任何额外的工作!
AndroidObservable,它提供了跟多的功能来配合Android的生命周期。
bindActivity()和bindFragment()方法默认使用AndroidSchedulers.mainThread()来执行观察者代码,
这两个方法会在Activity或者Fragment结束的时候通知被观察者停止发出新的消息。
AndroidObservable.bindActivity(this, retrofitService.getImage(url))
.subscribeOn(Schedulers.io())
.subscribe(bitmap -> myImageView.setImageBitmap(bitmap);
AndroidObservable.fromBroadcast()方法,它允许你创建一个类似BroadcastReceiver的Observable对象。
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
AndroidObservable.fromBroadcast(context, filter)
.subscribe(intent -> handleConnectivityChange(intent));
ViewObservable,使用它可以给View添加了一些绑定。
如果你想在每次点击view的时候都收到一个事件,可以使用ViewObservable.clicks(),
或者你想监听TextView的内容变化,可以使用ViewObservable.text()。
Retrofit
比如我们有一个请求是获取照片的,还有一个请求是获取元数据的,
我们就可以将这两个请求并发的发出,并且等待两个结果都返回之后再做处理:
转换成Observable
绝大多数时候Observable.just() 和 Observable.from() 能够帮助你从遗留代码中创建 Observable 对象:
private Object oldMethod() { ... }
public Observable<Object> newMethod() {
return Observable.just(oldMethod());
}
如果oldMethod()足够快是没有什么问题的,但是如果很慢呢?调用oldMethod()将会阻塞住他所在的线程。
为了解决这个问题,可以参考我一直使用的方法–使用defer()来包装缓慢的代码:
private Object slowBlockingMethod() { ... }
public Observable<Object> newMethod() {
return Observable.defer(() -> Observable.just(slowBlockingMethod()));
}
生命周期
我把最难的部分留在了最后。如何处理Activity的生命周期?主要就是两个问题:
1.在configuration改变(比如转屏)之后继续之前的Subscription。
比如你使用Retrofit发出了一个REST请求,接着想在listview中展示结果。如果在网络请求的时候用户旋转了屏幕怎么办?你当然想继续刚才的请求,但是怎么搞?
2.Observable持有Context导致的内存泄露
这个问题是因为创建subscription的时候,以某种方式持有了context的引用,尤其是当你和view交互的时候,这太容易发生!如果Observable没有及时结束,内存占用就会越来越大。
第二个问题的解决方案就是在生命周期的某个时刻取消订阅。一个很常见的模式就是使用CompositeSubscription来持有所有的Subscriptions,然后在onDestroy()或者onDestroyView()里取消所有的订阅。
而使用 RxJava 的话,代码是这样的:
结合RxBinding
RxBinding就是类似设置 OnClickListener 、设置 TextWatcher 这样的注册绑定对象的 API。
Button button = ...;
RxView.clickEvents(button) // 以 Observable 形式来反馈点击事件
.subscribe(new Action1<ViewClickEvent>() {
@Override
public void call(ViewClickEvent event) {
// Click handling
}
});
RxBinding 的目的:扩展性。通过 RxBinding 把点击监听转换成 Observable 之后,就有了对它进行扩展的可能。扩展的方式有很多,根据需求而定。一个例子是前面提到过的 throttleFirst() ,用于去抖动,也就是消除手抖导致的快速连环点击:
RxView.clickEvents(button)
.throttleFirst(500, TimeUnit.MILLISECONDS)
.subscribe(clickAction);
冷观察和热观察 https://www.aliyun.com/jiaocheng/14805.html?spm=5176.100033.2.11.X83JrU
Think of a hot Observable as a radio station. All of the listeners that are listening to it at this moment listen to the same song.
A cold Observable is a music CD. Many people can buy it and listen to it independently.
by Nickolay Tsvetinov
Cold Observable
Observable 的 just、creat、range、fromXXX 等操作符都能生成Cold Observable。
尽管 Cold Observable 很好,但是对于某些事件不确定何时发生以及不确定 Observable 发射的元素数量,那还得使用 Hot Observable。
比如:UI交互的事件、网络环境的变化、地理位置的变化、服务器推送消息的到达等等。
Cold Observable 如何转换成 Hot Observable?
1. 使用publish,生成 ConnectableObservable
使用 publish 操作符,可以让 Cold Observable 转换成 Hot Observable。
它将原先的 Observable 转换成 ConnectableObservable。
注意:生成的 ConnectableObservable 需要调用connect()才能真正执行
Subject和Processor
Subject 和 Processor 的作用是相同的。
Processor 是 RxJava2.x 新增的类,继承自 Flowable 支持背压控制。而 Subject 则不支持背压控制。
Subject 既是 Observable 又是 Observer(Subscriber)。这一点可以从 Subject 的源码上看到。
当 Subject 作为 Subscriber 时,它可以订阅目标 Cold Observable 使对方开始发送事件。同时它又作为Observable 转发或者发送新的事件,让 Cold Observable 借助 Subject 转换为 Hot Observable。
注意,Subject 并不是线程安全的,如果想要其线程安全需要调用toSerialized()方法。(在RxJava1.x的时代还可以用 SerializedSubject 代替 Subject,但是在RxJava2.x以后SerializedSubject不再是一个public class)
然而,很多基于 EventBus 改造的 RxBus 并没有这么做,包括我以前也写过这样的 RxBus :( 。这样的做法是非常危险的,因为会遇到并发的情况。
Hot Observable 如何转换成 Cold Observable?
RefCount操作符
如果所有的订阅者都取消订阅了,则数据流停止。如果重新订阅则重新开始数据流。
如果不是所有的订阅者都取消了订阅,只取消了部分。部分的订阅者重新开始订阅,则不会从头开始数据流
share操作符封装了publish().refCount()调用
======RxJava1.0和2.x的不同======
https://blog.csdn.net/lengqi0101/article/details/59552953
Rxjava2.0,出现了两种观察者模式:
Observable(被观察者)/Observer(观察者)
Flowable(被观察者)/Subscriber(观察者)
RxJava2.X中, Observeable用于订阅Observer ,是不支持背压的,
而 Flowable用于订阅Subscriber ,是支持背压(Backpressure)的。
Action
Action这类接口,在1.0中,这类接口是从Action0,Action1…往后排(数字代表可接受的参数),现在做出了改动
Rx1.0———–Rx2.0
Action1——–Action
Action1——–Consumer
Action2——–BiConsumer
后面的Action都去掉了,只保留了ActionN
去掉了Schedulers.immediate()这个线程环境
======RxJava1.0和2.x的不同======
RxBus
RxBus 名字看起来像一个库,但它并不是一个库,而是一种模式,
它的思想是使用 RxJava 来实现了 EventBus ,
而让你不再需要使用 Otto 或者 GreenRobot 的 EventBus
====EventBus3.0======
1.compile 'org.greenrobot:eventbus:3.0.0'
2.定义消息事件类
线程模型有4种
POSTING (默认) 表示事件处理函数的线程跟发布事件的线程在同一个线程。
MAIN 表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。
BACKGROUND 表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。
ASYNC 表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。
just 了解一下下
====EventBus3.0======
RxBus 继续
RxBus的写法 发生异常不取消订阅关系的写法/是否采取背压的写法
https://blog.csdn.net/u011271348/article/details/69946650
上述这种类似EventBus 事件总线(公交车)的写法
而这种写法有如下缺点
Event无论顺序还是时间上都某种程度上不太可控。
EventBus看似将你的程序解耦,但是又有些过了。
过多EventBus的代码会造成代码结构混乱,难以测试和追踪,违背了解耦的初衷。
这时如果有意或无意的造成了Nested Event。那情况会更糟。
怎样才是正确(正常?)的RxJava使用方式?
其实Jake大神也在GitHub的讨论上给出了一个答案:
所以应该是,每当你想发布一个Event在EventBus时,直接暴露一个Observable出来。每当你想接受一个Event时,找到这个Observable并且Subscribe他。
下面这个放弃RxBus系列 ,可以灵活进行1 to 1/n
https://www.jianshu.com/p/61631134498e
1 to 1/n 可以用share/replay 等操作符完美解决。 n to n 也可以用局部Subject。
- 由于我们将editText封装在Observable里,无论是create()方法还是使用RxBinding,都会持有这个View的强引用。造成内存泄漏。所以我们一定要在最后加入dispose()方法来释放。所以我推荐使用RxBinding,他已经帮我们在dispose()方法里写好了解除Listener的方法。
- 因为并没有使用publish操作符,导致多个Subscriber的时候还是有些许问题。可以考虑直接加入.share().
====上述大部分是RxJava1.0,少部分是2.x,乱,而且2.x相对1.0是独立的,改了很多=====
下面集中学习下2.0from简书nanchen系列
https://www.jianshu.com/p/a93c79e9f689
它组合了观察者模式,迭代器模式和函数式编程的优秀思想。
高效、干净和可扩展的方式来处理实时数据处理的这一场景。
RxJava 是 ReactiveX 在 Java 上的开源的实现。
RxJava 最核心的两个参与者 Observables(被观察者、事件源)和Subscribers(观察者)
Observables 发出一系列事件。Subscribers处理这些事件
RxJava 和 观察者模式有一点明显不同,就是如果一个Observable没有任何Subscriber,那么这个Observable不会发出任何事件
RJ 基础写法
Observable<String> myObservable = Observable.create(
new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> sub) {
sub.onNext("Hello, world!");
sub.onCompleted();
}
}
);
Subscriber<String> mySubscriber = new Subscriber<String>() {
@Override
public void onNext(String s) { System.out.println(s); }
@Override
public void onCompleted() { }
@Override
public void onError(Throwable e) { }
};
订阅建立:myObservable.subscribe(mySubscriber);
Observalbe 创建简写
Observable<String> myObservable = Observable.just("Hello, world!");
只关心Subscriber 的 onNext;方法专属化写法
Action1<String> onNextAction = new Action1<String>() {
@Override
public void call(String s) {
System.out.println(s);
}
};
Subscribe 有一个重载方法
myObservable.subscribe(onNextAction, onErrorAction, onCompleteAction);
我们不关心onError和onComplete
所以订阅可以写成 mObservable.subscrible(onNextAction);
上面的代码可以简写成
Observable.just("Hello, world!")
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
System.out.println(s);
}
});
这里有个需求:
1.修改打印出的内容
2.对内容进行过滤
3.不在onNext中做变动(真正的响应式)
这里就需要引进操作符了Operators进行变换操作
(摘,写的很好)
因为我希望我的Subscribers越轻量越好,
因为我有可能会在mainThread中运行subscriber。
另外,根据响应式函数编程的概念,Subscribers更应该做的事情是“响应”,
响应Observable发出的事件,而不是去修改。
如果我能在某些中间步骤中对“Hello World!”进行变换是不是很酷?
(摘,写的很好)
操作符 Operator 对 Observable进行变换
之一
map:
Observable.just("Hello, world!")
.map(new Func1<String, String>() {
@Override
public String call(String s) {
return s + " -Dan";
}
})
.subscribe(s -> System.out.println(s));
例如:subscriber并不关心返回的字符串,而是想要字符串的hash值
Observable.just("Hello, world!")
.map(new Func1<String, Integer>() {
@Override
public Integer call(String s) {
return s.hashCode();
}
})
.subscribe(i -> System.out.println(Integer.toString(i)));
前面说过,Subscriber做的事情越少越好,我们再增加一个map操作符
Observable.just("Hello, world!")
.map(s -> s.hashCode())
.map(i -> Integer.toString(i))
.subscribe(s -> System.out.println(s));
map可以进行多次变换,进行类型的切换,直至切换到subscriber最终需要的类型。
(摘,写的很好)
不服?
是不是觉得我们的例子太简单,不足以说服你?你需要明白下面的两点:
1.Observable和Subscriber可以做任何事情
Observable可以是一个数据库查询,Subscriber用来显示查询结果;
Observable可以是屏幕上的点击事件,Subscriber用来响应点击事件;
Observable可以是一个网络请求,Subscriber用来显示请求结果。
2.Observable和Subscriber是独立于中间的变换过程的。
在Observable和Subscriber中间可以增减任何数量的map。
整个系统是高度可组合的,操作数据是一个很简单的过程。
(摘,写的很好)
from
Observable.from()方法,它接收一个集合作为输入,然后每次输出一个元素给subscriber
map()是一对一的转化,而我现在的要求是一对多的转化。那怎么才能把一个 Student 转化成多个 Course 呢?
这个时候,就需要用 flatMap() 了
flatMap
Observable.flatMap()接收一个Observable的输出作为输入,同时输出另外一个Observable。
query("Hello, world!")
.flatMap(new Func1<List<String>, Observable<String>>() {
@Override
public Observable<String> call(List<String> urls) {
return Observable.from(urls);
}
})
.subscribe(url -> System.out.println(url));
flatMap输出的新的Observable正是我们在Subscriber想要接收的。
现在Subscriber不再收到List<String>,而是收到一些列单个的字符串,就像Observable.from()的输出一样。
query("Hello, world!")
.flatMap(urls -> Observable.from(urls))
.flatMap(new Func1<String, Observable<String>>() {
@Override
public Observable<String> call(String url) {
return getTitle(url);
}
})
.subscribe(title -> System.out.println(title));
可以多次flatMap
·filter()输出和输入相同的元素,并且会过滤掉那些不满足检查条件的。
·take()输出最多指定数量的结果。
.takeLast(N)只发射最后N个元素
.first()只发射第一个
.last()只发射最后一个
.skip(N)不发射前N个元素而是发射它后面的那些数据的序列
.skipLast()
.distinct 仅处理一次,可以处理去除重复的数据
.elementAt(N)仅从一个序列中发射第n个元素然后就完成了,这里是从0开始计的。
.sample(1000, TimeUnit.MILLISECONDS)定期发射Observable最近发射的数据项
·doOnNext()允许我们在每次输出一个元素之前做一些额外的事情,比如存库
query("Hello, world!")
.flatMap(urls -> Observable.from(urls))
.flatMap(url -> getTitle(url))
.filter(title -> title != null)
.take(5)
.doOnNext(title -> saveTitle(title))
.subscribe(title -> System.out.println(title));
·throttleFirst()
在每次事件触发后的一定时间间隔内丢弃新的事件。
常用作去抖动过滤,例如按钮的点击监听器:
RxView.clickEvents(button) // RxBinding 代码,后面的文章有解释
.throttleFirst(500, TimeUnit.MILLISECONDS) // 设置防抖间隔为 500ms
.subscribe(subscriber);
另外还有merge操作符
Observable<Integer> observable1 = Observable.just(1, 3, 5);
Observable<Integer> observable2 = Observable.just(2, 4, 6);
Observable.merge(observable1,observable2)
.subscribe(new Observer<Integer>() {
@Override
public void onCompleted() {
Log.i("wxl", "onCompleted");
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Integer integer) {
Log.i("wxl", "onNext=" + integer);
}
});
打印结果 135246.顺序打印完observable1、observable2
以及更多操作符,还可以自定义操作符
操作符 zip
合并两个或者多个Observables发射出的数据项,
根据指定的函数Func2变换它们,并发射一个新值。
Observable<Integer> observable1 = Observable.just(1, 3, 5);
Observable<Integer> observable2 = Observable.just(2, 4, 6, 9);
Observable.zip(observable1, observable2, new Func2<Integer, Integer, Integer>
@Override
public Integer call(Integer integer, Integer integer2) {
return integer + integer2;
}
})
.subscribe(new Observer<Integer>() {
@Override
public void onCompleted() {
Log.i("wxl", "onCompleted");
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Integer integer) {
Log.i("wxl", "onNext=" + integer);
}
});
打印结果3 7 11 onCompleted
还有join startWith 对数据序列进行插入操作
错误处理
这种模式有以下几个优点:
1.只要有异常发生onError()一定会被调用
这极大的简化了错误处理。只需要在一个地方处理错误即可以。
2.操作符不需要处理异常
将异常处理交给订阅者来做,Observerable的操作符调用链中一旦有一个抛出了异常,就会直接执行onError()方法。
3.你能够知道什么时候订阅者已经接收了全部的数据。
使用RxJava,Observable对象根本不需要知道如何处理错误!
操作符也不需要处理错误状态-一旦发生错误,就会跳过当前和后续的操作符。
所有的错误处理都交给订阅者来做。
调度器Scheduler
使用RxJava,你可以使用subscribeOn()指定观察者代码运行的线程,
使用observerOn()指定订阅者运行的线程
myObservableServices.retrieveImage(url)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bitmap -> myImageView.setImageBitmap(bitmap));
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 主线程运行。
有了这几个 Scheduler ,就可以使用 subscribeOn() 和 observeOn() 两个方法来对线程进行控制了。
* subscribeOn(): 事件产生的线程。
* observeOn(): 事件消费的线程。
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()即可。
Subscriber.onStart() 相对应的,有一个方法 Observable.doOnSubscribe() 。
它和 Subscriber.onStart() 同样是在 subscribe() 调用后而且在事件发送前执行,但区别在于它可以指定线程。
默认情况下,doOnSubscribe() 执行在 subscribe() 发生的线程;而如果在 doOnSubscribe() 之后有 subscribeOn() 的话,它将执行在离它最近的 subscribeOn() 所指定的线程。
Observable.create(onSubscribe)
.subscribeOn(Schedulers.io())
.doOnSubscribe(new Action0() {
@Override
public void call() {
progressBar.setVisibility(View.VISIBLE); // 需要在主线程执行
}
})
.subscribeOn(AndroidSchedulers.mainThread()) // 指定主线程
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
当调用Observable.subscribe(),会返回一个Subscription对象。
这个对象代表了被观察者和订阅者之间的联系。
subscription.unsubscribe();
RxJava的另外一个好处就是它处理unsubscribing的时候,会停止整个调用链。
如果你使用了一串很复杂的操作符,调用unsubscribe将会在他当前执行的地方终止。不需要做任何额外的工作!
RxAndroid
retrofitService.getImage(url)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bitmap -> myImageView.setImageBitmap(bitmap));
如果你已经创建了自己的Handler,你可以使用HandlerThreadScheduler1将一个调度器链接到你的handler上。
AndroidObservable,它提供了跟多的功能来配合Android的生命周期。
bindActivity()和bindFragment()方法默认使用AndroidSchedulers.mainThread()来执行观察者代码,
这两个方法会在Activity或者Fragment结束的时候通知被观察者停止发出新的消息。
AndroidObservable.bindActivity(this, retrofitService.getImage(url))
.subscribeOn(Schedulers.io())
.subscribe(bitmap -> myImageView.setImageBitmap(bitmap);
AndroidObservable.fromBroadcast()方法,它允许你创建一个类似BroadcastReceiver的Observable对象。
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
AndroidObservable.fromBroadcast(context, filter)
.subscribe(intent -> handleConnectivityChange(intent));
ViewObservable,使用它可以给View添加了一些绑定。
如果你想在每次点击view的时候都收到一个事件,可以使用ViewObservable.clicks(),
或者你想监听TextView的内容变化,可以使用ViewObservable.text()。
Retrofit
通常
@GET("/user/{id}/photo")
void getUserPhoto(@Path("id") int id, Callback<Photo> cb);
结合RxJava
@GET("/user/{id}/photo")
Observable<Photo> getUserPhoto(@Path("id") int id);
Retrofit对Observable的支持使得它可以很简单的将多个REST请求结合起来。
比如我们有一个请求是获取照片的,还有一个请求是获取元数据的,
我们就可以将这两个请求并发的发出,并且等待两个结果都返回之后再做处理:
Observable.zip(
service.getUserPhoto(id),
service.getPhotoMetadata(id),
(photo, metadata) -> createPhotoWithData(photo, metadata))
.subscribe(photoWithData -> showPhoto(photoWithData));
遗留代码,运行极慢的代码
转换成Observable
绝大多数时候Observable.just() 和 Observable.from() 能够帮助你从遗留代码中创建 Observable 对象:
private Object oldMethod() { ... }
public Observable<Object> newMethod() {
return Observable.just(oldMethod());
}
如果oldMethod()足够快是没有什么问题的,但是如果很慢呢?调用oldMethod()将会阻塞住他所在的线程。
为了解决这个问题,可以参考我一直使用的方法–使用defer()来包装缓慢的代码:
private Object slowBlockingMethod() { ... }
public Observable<Object> newMethod() {
return Observable.defer(() -> Observable.just(slowBlockingMethod()));
}
生命周期
我把最难的部分留在了最后。如何处理Activity的生命周期?主要就是两个问题:
1.在configuration改变(比如转屏)之后继续之前的Subscription。
比如你使用Retrofit发出了一个REST请求,接着想在listview中展示结果。如果在网络请求的时候用户旋转了屏幕怎么办?你当然想继续刚才的请求,但是怎么搞?
2.Observable持有Context导致的内存泄露
这个问题是因为创建subscription的时候,以某种方式持有了context的引用,尤其是当你和view交互的时候,这太容易发生!如果Observable没有及时结束,内存占用就会越来越大。
第二个问题的解决方案就是在生命周期的某个时刻取消订阅。一个很常见的模式就是使用CompositeSubscription来持有所有的Subscriptions,然后在onDestroy()或者onDestroyView()里取消所有的订阅。
Subscription subscription = Observable.just("Hello", "RxJava", "WuXiaolong")
.subscribe(new Observer<String>() {
@Override
public void onCompleted() {
Log.i("wxl", "onCompleted");
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(String s) {
Log.i("wxl", "onNext=" + s);
}
});
addSubscription(subscription);
addSubscription方法可以放到父类:
private CompositeSubscription mCompositeSubscription;
public void addSubscription(Subscription subscription) {
if (this.mCompositeSubscription == null) {
this.mCompositeSubscription = new CompositeSubscription();
}
this.mCompositeSubscription.add(subscription);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (this.mCompositeSubscription != null) {
this.mCompositeSubscription.unsubscribe();
}
}
结合Retrofit 用例:
@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);
...
getToken()
.flatMap(new Func1<String, Observable<User>>() {
@Override
public Observable<User> onNext(String token) {
return getUser(token, 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
...
}
});
多个请求,下一个请求依赖上一个请求的结果
结合RxBinding
RxBinding就是类似设置 OnClickListener 、设置 TextWatcher 这样的注册绑定对象的 API。
Button button = ...;
RxView.clickEvents(button) // 以 Observable 形式来反馈点击事件
.subscribe(new Action1<ViewClickEvent>() {
@Override
public void call(ViewClickEvent event) {
// Click handling
}
});
RxBinding 的目的:扩展性。通过 RxBinding 把点击监听转换成 Observable 之后,就有了对它进行扩展的可能。扩展的方式有很多,根据需求而定。一个例子是前面提到过的 throttleFirst() ,用于去抖动,也就是消除手抖导致的快速连环点击:
RxView.clickEvents(button)
.throttleFirst(500, TimeUnit.MILLISECONDS)
.subscribe(clickAction);
冷观察和热观察 https://www.aliyun.com/jiaocheng/14805.html?spm=5176.100033.2.11.X83JrU
Think of a hot Observable as a radio station. All of the listeners that are listening to it at this moment listen to the same song.
A cold Observable is a music CD. Many people can buy it and listen to it independently.
by Nickolay Tsvetinov
Cold Observable
Observable 的 just、creat、range、fromXXX 等操作符都能生成Cold Observable。
尽管 Cold Observable 很好,但是对于某些事件不确定何时发生以及不确定 Observable 发射的元素数量,那还得使用 Hot Observable。
比如:UI交互的事件、网络环境的变化、地理位置的变化、服务器推送消息的到达等等。
Cold Observable 如何转换成 Hot Observable?
1. 使用publish,生成 ConnectableObservable
使用 publish 操作符,可以让 Cold Observable 转换成 Hot Observable。
它将原先的 Observable 转换成 ConnectableObservable。
注意:生成的 ConnectableObservable 需要调用connect()才能真正执行
Subject和Processor
Subject 和 Processor 的作用是相同的。
Processor 是 RxJava2.x 新增的类,继承自 Flowable 支持背压控制。而 Subject 则不支持背压控制。
Subject 既是 Observable 又是 Observer(Subscriber)。这一点可以从 Subject 的源码上看到。
当 Subject 作为 Subscriber 时,它可以订阅目标 Cold Observable 使对方开始发送事件。同时它又作为Observable 转发或者发送新的事件,让 Cold Observable 借助 Subject 转换为 Hot Observable。
注意,Subject 并不是线程安全的,如果想要其线程安全需要调用toSerialized()方法。(在RxJava1.x的时代还可以用 SerializedSubject 代替 Subject,但是在RxJava2.x以后SerializedSubject不再是一个public class)
然而,很多基于 EventBus 改造的 RxBus 并没有这么做,包括我以前也写过这样的 RxBus :( 。这样的做法是非常危险的,因为会遇到并发的情况。
Hot Observable 如何转换成 Cold Observable?
RefCount操作符
如果所有的订阅者都取消订阅了,则数据流停止。如果重新订阅则重新开始数据流。
如果不是所有的订阅者都取消了订阅,只取消了部分。部分的订阅者重新开始订阅,则不会从头开始数据流
share操作符封装了publish().refCount()调用
======RxJava1.0和2.x的不同======
https://blog.csdn.net/lengqi0101/article/details/59552953
Rxjava2.0,出现了两种观察者模式:
Observable(被观察者)/Observer(观察者)
Flowable(被观察者)/Subscriber(观察者)
RxJava2.X中, Observeable用于订阅Observer ,是不支持背压的,
而 Flowable用于订阅Subscriber ,是支持背压(Backpressure)的。
Action
Action这类接口,在1.0中,这类接口是从Action0,Action1…往后排(数字代表可接受的参数),现在做出了改动
Rx1.0———–Rx2.0
Action1——–Action
Action1——–Consumer
Action2——–BiConsumer
后面的Action都去掉了,只保留了ActionN
去掉了Schedulers.immediate()这个线程环境
======RxJava1.0和2.x的不同======
RxBus
RxBus 名字看起来像一个库,但它并不是一个库,而是一种模式,
它的思想是使用 RxJava 来实现了 EventBus ,
而让你不再需要使用 Otto 或者 GreenRobot 的 EventBus
====EventBus3.0======
1.compile 'org.greenrobot:eventbus:3.0.0'
2.定义消息事件类
public class MessageEvent{
private String message;
public MessageEvent(String message){
this.message=message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
3.注册与解除
onCreate
EventBus.getDefault().register(this);
onDestroy
if(EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().unregister(this);
}
4.发送事件
EventBus.getDefault().post(messageEvent);
5.处理事件
@Subscribe(threadMode = ThreadMode.MAIN)
public void XXX(MessageEvent messageEvent) {
...
}
名字可以随意取,但需要加一个注解 @Subscribe,并且指定线程模型
线程模型有4种
POSTING (默认) 表示事件处理函数的线程跟发布事件的线程在同一个线程。
MAIN 表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。
BACKGROUND 表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。
ASYNC 表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。
just 了解一下下
====EventBus3.0======
RxBus 继续
RxBus的写法 发生异常不取消订阅关系的写法/是否采取背压的写法
https://blog.csdn.net/u011271348/article/details/69946650
上述这种类似EventBus 事件总线(公交车)的写法
而这种写法有如下缺点
Event无论顺序还是时间上都某种程度上不太可控。
EventBus看似将你的程序解耦,但是又有些过了。
过多EventBus的代码会造成代码结构混乱,难以测试和追踪,违背了解耦的初衷。
这时如果有意或无意的造成了Nested Event。那情况会更糟。
怎样才是正确(正常?)的RxJava使用方式?
其实Jake大神也在GitHub的讨论上给出了一个答案:
所以应该是,每当你想发布一个Event在EventBus时,直接暴露一个Observable出来。每当你想接受一个Event时,找到这个Observable并且Subscribe他。
下面这个放弃RxBus系列 ,可以灵活进行1 to 1/n
https://www.jianshu.com/p/61631134498e
1 to 1/n 可以用share/replay 等操作符完美解决。 n to n 也可以用局部Subject。
- 由于我们将editText封装在Observable里,无论是create()方法还是使用RxBinding,都会持有这个View的强引用。造成内存泄漏。所以我们一定要在最后加入dispose()方法来释放。所以我推荐使用RxBinding,他已经帮我们在dispose()方法里写好了解除Listener的方法。
- 因为并没有使用publish操作符,导致多个Subscriber的时候还是有些许问题。可以考虑直接加入.share().
====上述大部分是RxJava1.0,少部分是2.x,乱,而且2.x相对1.0是独立的,改了很多=====
下面集中学习下2.0from简书nanchen系列
https://www.jianshu.com/p/a93c79e9f689