CSDN:https://blog.csdn.net/chenzhen200638/article/details/81569036
用一句话描述RxJava的本质:
- RxJava采用观察者模式,实现了数据发射(emitter)和数据接收并消费(consumer)响应式开发模式,并能灵活切换任务线程。
观察者模式 RxJava 以观察者模式为骨架,在 2.0 中依旧如此,不过此次更新中,出现了两种观察者模式:
- Observable ( 被观察者 ) / Observer ( 观察者 )
- Flowable (被观察者)/ Subscriber (观察者)
在 RxJava 2.x 中,Observable 用于订阅 Observer,不再支持背压(1.x 中可以使用背压策略),而 Flowable 用于订阅 Subscriber , 是支持背压(Backpressure)的。
1.创建Observable被观察者对象,并给订阅者发射数据,获得数据后给界面展示.
public Observable queryStudentWithObservable(int age){
return Observable.create( (ObservableOnSubscribe) e -> {
Realm realm = Realm.getDefaultInstance();
Student student = null;
try {
realm.beginTransaction();
student = realm.where(Student.class).equalTo("age", age).findFirst();
realm.commitTransaction();
} catch (Exception ex) {
ex.printStackTrace();
}finally {
if(student != null){
e.onNext(student);
}else{
e.onError(new Throwable("没有查到数据!"));
}
}
});
}
public void queryStudentWithObservable(){
container.removeAllViews();
RealmService.getRealmService().queryStudentBy(15).subscribe(
student-> container.addView(buildTextView(student.toString())),
throwable-> Toast.makeText(this,throwable.toString(),Toast.LENGTH_LONG).show())
.dispose();
}
来看看subscibe的源码实现, subscribe的参数有两个:Consumer onNext,Consumer onError;
@CheckReturnValue
@SchedulerSupport(SchedulerSupport.NONE)
public final Disposable subscribe(
Consumer super T> onNext,
Consumer super Throwable> onError,
Action onComplete, Consumer super Disposable> onSubscribe) {
... ...
//核心代码,实际上就是新构造了一个LambdaObserver定阅数据源;
LambdaObserver ls = new LambdaObserver(onNext, onError, onComplete, onSubscribe);
subscribe(ls);
return ls;
}
1.创建Flowable被观察者对象,并给订阅者发射数据,获得数据后给界面展示.
public Flowable queryStudentWithFlowable(int age){
return Flowable.create( e -> {
Realm realm = Realm.getDefaultInstance();
Student student = null;
try {
realm.beginTransaction();
student = realm.where(Student.class).equalTo("age", age).findFirst();
realm.commitTransaction();
} catch (Exception ex) {
ex.printStackTrace();
}finally {
if(student != null){
e.onNext(student);
}else{
e.onError(new Throwable("没有查到数据!"));
}
}
}, BackpressureStrategy.BUFFER);
}
public void queryStudentWithObservable(){
RealmService.getRealmService().queryStudentWithFlowable(15).subscribe(
student-> container.addView(buildTextView(student.toString())),
throwable-> Toast.makeText(this,throwable.toString(),Toast.LENGTH_LONG).show())
.dispose();
}
应用场景: 只发射一条单一的数据,或者一条异常通知,不能发射完成通知,其中数据与通知只能发射一个结果.
实例: 查询数据库,成功则更新UI,失败则提示没有查询到数据.
- Single.fromCallable(emitter->{})
或者
- Single.create(emitter->{});
/**
*创建一个Single的数据源,根据年纪参数查询学生数据;
**/
public Single findStudentBy(int age) {
return Single.fromCallable(() -> {
Realm realm = Realm.getDefaultInstance();
Student student = null;
try {
realm.beginTransaction();
student = realm.where(Student.class).equalTo("age", age).findFirst();
realm.commitTransaction();
} catch (Exception ex) {
ex.printStackTrace();
}
if(student == null){
throw new Exception("没有查到数据!");
}
return student;
});
}
/**
*查询年纪为15岁的学生
**/
public void singleQuerySample() {
container.removeAllViews();
Single s15 = RealmService.getRealmService().findStudentBy(15);
s15.subscribe(
student -> container.addView(buildTextView(student.toString())),
error -> Toast.makeText(
MainActivity.this,
error.toString(),
Toast.LENGTH_LONG).show());
}
应用场景: 只发射一条完成通知,或者一条异常通知,不能发射数据,其中完成通知与异常通知只能发射一个
例子:添加学生数据,不关注成功失败,只关注事件完成与否。
/**
* 添加学生数据,创建一个Completeble对象;
**/
public Completable addStudent(Student student) {
//注意,这里是通过fromAction来生成一个Completeble,即只关注单一的动作是否完成.
return Completable.fromAction(() -> {
Realm realm = Realm.getDefaultInstance();
try {
realm.executeTransaction(r -> {
Student s = realm.createObject(Student.class, UUID.randomUUID().toString());
s.setName(student.getName());
s.setAddress(student.getAddress());
s.setAge(student.getAge());
realm.copyFromRealm(s);
});
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (realm != null) {
realm.close();
}
}
});
}
或者,也可以通过Completeble.create()来创建Completeble.
public Completable addStudent(Student student) {
return Completable.create( e -> {
Realm realm = Realm.getDefaultInstance();
try {
realm.executeTransaction(r -> {
Student s = realm.createObject(Student.class, UUID.randomUUID().toString());
s.setName(student.getName());
s.setAddress(student.getAddress());
s.setAge(student.getAge());
realm.copyFromRealm(s);
});
//结束业务流程,发送通知完成了;
e.onComplete();
} catch (Exception ex) {
ex.printStackTrace();
//发生异常,发送一个异常通知;
e.onError(ex);
} finally {
if (realm != null) {
realm.close();
}
}
});
}
然后在Activity中,调用这个接口添加学生数据,完成添加动作之后,更新数据和ui.
/**
*Activity中: 构造学生数据
**/
public void buildStudentDatas() {
Student a = new Student();
a.setAge(14);
a.setAddress("中国北京");
a.setName("周浩");
Student b = new Student();
b.setAge(15);
b.setAddress("深圳小牛在线");
b.setName("陈真");
Student c = new Student();
c.setAge(16);
c.setAddress("中国上海");
c.setName("文忠湖");
//每添加一个学生的数据后,查询并更新ui.
RealmService.getRealmService().addStudent(a).subscribe(this::queryStudent);
RealmService.getRealmService().addStudent(b).subscribe(this::queryStudent);
RealmService.getRealmService().addStudent(c).subscribe(this::queryStudent);
}
/**
*查询所有学生数据,并更新界面
*/
public void queryStudent() {
container.removeAllViews();
RealmService.getRealmService().findAllStudent()
.subscribe(students -> {
for (Student s : students) {
container.addView(buildTextView(s.toString()));
}
});
}
应用场景: 可发射一条单一的数据,以及发射一条完成通知,或者一条异常通知(onComplte() 和 onError() 只能发射其中一个消息,同时发送的话onComplete()无效),其中完成通知和异常通知只能发射一个,发射数据只能在发射完成通知或者异常通知之前,否则发射数据无效。
例子: 根据年纪来查询学生数据,查到了发送数据,没查到要么发送完成消息,要么发送异常信息;
public Maybe queryStudentWithMaybe(int age){
return Maybe.create( e -> {
Realm realm = Realm.getDefaultInstance();
Student student = null;
try {
realm.beginTransaction();
student = realm.where(Student.class).equalTo("age", age).findFirst();
realm.commitTransaction();
} catch (Exception ex) {
ex.printStackTrace();
}finally {
if(student != null){
//如果查询到结果发送数据;
e.onSuccess(student);
}else{
//如果没有查询到数据,发送异常消息;
e.onError(new Throwable("没有查到数据!"));
}
//事件完成发送完成通知;
//e.onComplete();
}
});
}
Activity中接收数据,并在ui中展示结果
public void queryStudentWithMaybe() {
container.removeAllViews();
RealmService.getRealmService().queryStudentWithMaybe(15).subscribe(
student -> container.addView(buildTextView(student.toString())),
error -> Toast.makeText(MainActivity.this, error.toString(), Toast.LENGTH_LONG).show())
.dispose();
}
subscribeOn 用于指定 subscribe() 上游所发生的线程,从源码角度可以看出,内部线程调度是通过 ObservableSubscribeOn 来实现的。
@SchedulerSupport(SchedulerSupport.CUSTOM)
public final Observable subscribeOn(Scheduler scheduler) {
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
return RxJavaPlugins.onAssembly(new ObservableSubscribeOn(this, scheduler));
}
observeOn 方法用于指定下游 Observer 回调发生的线程。
@SchedulerSupport(SchedulerSupport.CUSTOM)
public final Observable observeOn(Scheduler scheduler, boolean delayError, int bufferSize) {
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
ObjectHelper.verifyPositive(bufferSize, "bufferSize");
return RxJavaPlugins.onAssembly(new ObservableObserveOn(this, scheduler, delayError, bufferSize));
}
4.3.1、RxJava 内置的线程调度器的确可以让我们的线程切换得心应手,但其中也有些需要注意的地方。
4.3.2、RxJava 中,已经内置了很多线程选项供我们选择,例如有:
- Schedulers.io() 代表io操作的线程, 通常用于网络,读写文件等io密集型的操作;
- Schedulers.computation() 代表CPU计算密集型的操作, 例如需要大量计算的操作;
- Schedulers.newThread() 代表一个常规的新线程;
- AndroidSchedulers.mainThread() 代表Android的主线程
4.3.3、使用compose包装代码,重复利用代码:
相信小伙伴在使用RXJava与Retrofit请求网络时,都有遇到过这样的场景,在IO线程请求网络解析数据,接着返回主线程setData、更新View试图,那么也肯定熟悉下面这几句代码:
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
如果网络请求的次数比较少, 作为一名不拘小节.
但是如果请求网络的biz次数多起来了,又不想去破环RX的链式结构,那么怎么办呢?
其实使用compose操作符每次仅书写一行代码就能完成工作线程的切换了!
final Observable.Transformer schedulersTransformer = new Observable.Transformer() {
@Override public Object call(Object observable) {
return ((Observable) observable)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
}
};
仅仅通过.compose(schedulersTransformer)行代码就完成了线程切换.
RetrofitClient.singletonDemoService("http://gank.io/api/random/data/")
.requestNet("福利","1")
.compose(schedulersTransformer)
.subscribe(subscriber);
Map是RxJava中最简单的一个变换操作符,它的作用是将上游发送过来的事件都去应用一个函数,让每一个事件都按照该函数去变化,下游接收到事件时,就变成了变化过后的事件。
例子:把数字转换成字符串
官方文档:http://reactivex.io/documentation/operators/map.html
Observable.create(e -> {
e.onNext(1);
e.onNext(2);
e.onNext(3);}
).map( upstream-> "0x"+String.valueOf(upstream))
.subscribe(result-> container.addView(buildTextView(result)));
输出结果:
0x1
0x2
0x3
FlatMap,这个操作符和刚才的Map有什么区别呢?
FlatMap可以将上游发送过来的数据,变换为多个数据,然后合并为一个事件发送到下游。
例子: 所数据源 1, 2, 3变换成多个数据发送;
官方文档: http://reactivex.io/documentation/operators/flatmap.html
container.removeAllViews();
Observable.create(e -> {
e.onNext(1);
e.onNext(2);
e.onNext(3);}
).flatMap( upstream-> {
List source = new ArrayList<>();
for(int i=0; i<3; i++){
source.add("0x"+ upstream);
}
return Observable.fromIterable(source);
})
.subscribe(result-> container.addView(buildTextView(result)));
输出结果: 无序输出
0x1
0x1
0x1
0x2
0x2
0x3
0x3
0x2
0x3
ConcatMap和FlatMap一样,只不过一个是有序,一个是无序而已,我们直接把上边的代码做一个更改.
container.removeAllViews();
Observable.create(e -> {
e.onNext(1);
e.onNext(2);
e.onNext(3);}
).concatMap( upstream-> {
List source = new ArrayList<>();
for(int i=0; i<3; i++){
source.add("0x"+ upstream);
}
return Observable.fromIterable(source);
})
.subscribe(result-> container.addView(buildTextView(result)));
输出结果: 有序输出
0x1
0x1
0x1
0x2
0x2
0x3
0x3
0x3
0x3
构建一个 String 发射器 和 Integer 发射器
//创建 String 发射器
private Observable getStringObservable() {
return Observable.create( e->{
e.onNext("A");
e.onNext("B");
e.onNext("C");
});
}
//创建 Integer 发射器
private Observable getIntegerObservable() {
return Observable.create(e-> {
e.onNext(1);
e.onNext(2);
e.onNext(3);
e.onNext(4);
e.onNext(5);
});
}
使用 zip 操作符,打包合并两个事件
private void zip(){
Observable.zip(
getStringObservable(),
getIntegerObservable(),
(string,integer)->string+integer).subscribe(
result-> container.addView(buildTextView(result))
);
}
输出结果: 多出来的 4,5 被抛弃了,没有匹配的数据合并;
A1
A2
A3
interval操作符是每隔一段时间就产生一个数字,这些数字从0开始,一次递增1直至无穷大;
public void interval(){
Flowable.interval(1, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result-> container.addView(buildTextView(String.valueOf(result))));
或者
Observable.interval(1, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result-> container.addView(buildTextView(String.valueOf(result))));
}
输出结果:
0
1
2
3
...
n
利用interval实现倒计时的功能, 倒计时5秒钟
public void countDown(){
Observable.interval(1, TimeUnit.SECONDS)
.map(source-> 5-source)
.observeOn(AndroidSchedulers.mainThread())
.take(5)
.subscribe(result-> container.addView(buildTextView(String.valueOf(result))));
}
输出结果:
5
4
3
2
1
该操作符用来重复发送数据源
- repeat( ) 无限重复
- repeat( int time ) 设定重复的次数
private void repeat(){
Observable.just(1,2)
.repeat(2)
.subscribe(result-> container.addView(buildTextView(String.valueOf(result))));
}
输出结果:
1
2
1
2
range 发射特定整数序列的 Observable
- range( int start , int end ) start :开始的值 , end :结束的值
private void range(){
Observable
.range( 1 , 5 )
.subscribe(result-> container.addView(buildTextView(String.valueOf(result))));
}
输出结果:
1
2
3
4
5
由一个数组充当数据源发射到订阅者
private void fromArray(){
Integer[] items = {0, 1, 2, 3, 4, 5};
Observable
.fromArray(items)
.subscribe(result-> container.addView(buildTextView(String.valueOf(result))));
}
输出结果:
1
2
3
4
5
由一个集合充当数据源发射到订阅者
public void fromIterable(){
List list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
Observable
.fromIterable(list)
.subscribe(result-> container.addView(buildTextView(String.valueOf(result))));
}
输出结果:
a
b
c
延迟数据源的发射
Observable
.just(1, 2, 3)
.delay(3, TimeUnit.SECONDS) //延迟3秒钟,然后在发射数据
.subscribe(result-> container.addView(buildTextView(String.valueOf(result))));
RxJava是一个观察者模式的架构,当这个架构中被观察者(Observable)和观察者(Subscriber)处在不同的线程环境中时,由于者各自的工作量不一样,导致它们产生事件和处理事件的速度不一样,这就会出现两种情况:
需要强调两点:
RxJava2.X中,Observeable用于订阅Observer,是不支持背压的,而Flowable用于订阅Subscriber,是支持背压(Backpressure)的。
onBackpressureBuffer:默认情况下缓存所有的数据,不会丢弃数据,这个方法可以解决背压问题,但是它有像 Observable 一样的缺点,缓存数据太多,占用太多内存。
onBackpressureBuffer(int capacity) :设置缓存队列大小,但是如果缓冲数据超过了设置的值,就会报错,发生崩溃。
Flowable的三种Backpressure策略:
- BackpressureStrategy.BUFFER
- BackpressureStrategy.DROP
- BackpressureStrategy.LATEST
onBackpressureBuffer()不丢弃数据的处理方式。把上游收到的全部缓存下来,等下游来请求再发给下游。相当于一个水库。但上游太快,水库(buffer)就会溢出。
private void backPressure(){
Flowable.interval( 1 , TimeUnit.MILLISECONDS)
.onBackpressureBuffer( 100) //设置缓冲队列大小为 1000
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result-> container.addView(buildTextView(String.valueOf(result))));
}
上面代码运行一段时间后,会报错:
08-13 11:28:57.262 32675-32675/realm.com.xn.realmdatabase E/AndroidRuntime:
FATAL EXCEPTION: main Process: realm.com.xn.realmdatabase,
PID: 32675 io.reactivex.exceptions.OnErrorNotImplementedException: Buffer is full
通过日志可以看出,缓冲区已经满了.
注:onBackpressureLatest就是只保留最新的事件
git地址:https://github.com/JakeWharton/RxBinding
需要的库清单, 在gradle中添加以下库
implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1'
implementation 'com.jakewharton.rxbinding2:rxbinding-support-v4:2.1.1'
implementation 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.1.1'
implementation 'com.jakewharton.rxbinding2:rxbinding-design:2.1.1'
implementation 'com.jakewharton.rxbinding2:rxbinding-recyclerview-v7:2.1.1'
implementation 'com.jakewharton.rxbinding2:rxbinding-leanback-v17:2.1.1'
RxView.clicks(this.findViewById(R.id.addStudent))
.throttleFirst(2,TimeUnit.SECONDS)
.subscribe(v-> addStudent());
按钮的长按时间监听
RxView.longClicks(this.findViewById(R.id.Merge))
.throttleFirst(2,TimeUnit.SECONDS)
.subscribe(v-> merge());
ListView 单击事件
ListView listView = this.findViewById(R.id.list);
RxAdapterView.itemClicks(listView).subscribe(index-> onItemClick(index));
ListView 长按事件
ListView listView = this.findViewById(R.id.list);
RxAdapterView.itemLongClicks(listView).subscribe(index-> onItemClick(index));
checkBox = (CheckBox) findViewById( R.id.checkbox );
RxCompoundButton.checkedChanges(checkBox)
.subscribe( status-> {
button.setEnabled(status);
button.setBackgroundResource( status ? R.color.button_yes : R.color.button_no );
}) ;
搜索的时候,关键词联想功能 ,debounce()在一定的时间内没有操作就会发送事件;
实现数据库根据输入学生姓名匹配搜索功能
editText = (EditText) findViewById( R.id.editText );
listView = (ListView) findViewById( R.id.listview );
final ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_expandable_list_item_1 );
listView.setAdapter(adapter);
RxTextView.textChanges(editText)
.debounce( 600 , TimeUnit.MILLISECONDS )
.observeOn( Schedulers.io() )
.map(charSequence->{
//根据输入的内容,模糊查询学生信息
List students =
RealmService.getRealmService().queryStudentBy(charSequence);
return students;
})
.subscribe(new Action1>() {
@Override
public void call(List students) {
adapter.clear();
adapter.addAll(students);
adapter.notifyDataSetChanged();
}
}) ;
RxJava使我们很方便的使用链式编程,代码看起来既简洁又优雅。但是RxJava使用起来也是有副作用的,使用越来越多的订阅,内存开销也会变得很大,稍不留神就会出现内存溢出的情况,这篇文章就是介绍Rxjava使用过程中应该注意的事项。
String rmName = rmNameTextView.getText().toString();
RealmService.getRealmService()
.deleteStudentBy(rmName)
.subscribe(this::queryStudent)
.dispose();//也可以在onDestroy中销毁
或者
@Override
protected void onDestroy() {
super.onDestroy();
//取消订阅
if ( subscription != null ){
subscription.unsubscribe();
}
}
在android studio 里面添加引用
compile ‘com.trello:rxlifecycle-components:0.6.1’
让你的activity继承RxActivity,RxAppCompatActivity,RxFragmentActivity
让你的fragment继承RxFragment,RxDialogFragment;下面的代码就以RxAppCompatActivity举例
在子类使用Observable中的compose操作符,调用完成Observable发布的事件和当前的组件绑定,实现生命周期同步。从而实现当前组件生命周期结束时,自动取消对Observable订阅。
public class MainActivity extends RxAppCompatActivity {
TextView textView ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
//循环发送数字
Observable.interval(0, 1, TimeUnit.SECONDS)
.subscribeOn( Schedulers.io())
//这个订阅关系跟Activity绑定,Observable 和activity生命周期同步
.compose(this.bindToLifecycle())
.observeOn( AndroidSchedulers.mainThread())
.subscribe( time->textView.setText( String.valueOf( aLong ) ) );
}
}
上面的代码是Observable循环的发送数字,并且在textview中显示出来
从上面的例子可以看出bindToLifecycle() 方法可以使Observable发布的事件和当前的Activity绑定,实现生命周期同步。也就是Activity 的 onDestroy() 方法被调用后,Observable 的订阅关系才解除。那能不能指定在Activity其他的生命状态和订阅关系保持同步,答案是有的。就是bindUntilEvent()方法
ActivityEvent.CREATE: 在Activity的onCreate()方法执行后,解除绑定。
ActivityEvent.START:在Activity的onStart()方法执行后,解除绑定。
ActivityEvent.RESUME:在Activity的onResume()方法执行后,解除绑定。
ActivityEvent.PAUSE: 在Activity的onPause()方法执行后,解除绑定。
ActivityEvent.STOP:在Activity的onStop()方法执行后,解除绑定。
ActivityEvent.DESTROY:在Activity的onDestroy()方法执行后,解除绑定。
//循环发送数字
Observable.interval(0, 1, TimeUnit.SECONDS)
.subscribeOn( Schedulers.io())
//当Activity执行Onstop()方法是解除订阅关系
.compose(this.bindUntilEvent(ActivityEvent.STOP ))
.observeOn( AndroidSchedulers.mainThread())
.subscribe(time->textView.setText( String.valueOf( aLong ) ));
FragmentEvent 这个类是专门处理订阅事件与Fragment生命周期同步
public enum FragmentEvent {
ATTACH,
CREATE,
CREATE_VIEW,
START,
RESUME,
PAUSE,
STOP,
DESTROY_VIEW,
DESTROY,
DETACH
}
可以看出FragmentEvent 和ActivityEvent 类似,都是枚举类,用法是一样的.