#RxJava学习笔记
本文旨在梳理下RxJava的相关知识,所谓好记性不如烂笔头,方便日后有需要可以迅速查阅,像大多数学习RxJava的童鞋一样,推荐 给 Android 开发者的 RxJava 详解 一文,相信这篇文章对于需要入门的你再合适不过。
本文目录如下:
文章目录
RxJava简介
RxJava is a Java VM implementation of Reactive Extensions: a library for composing asynchronous and event-based programs by using observable sequences. —— GitHub上RxJava自我介绍
“异步"以及"基于事件”,正是对RxJava的高度概括,这两个属性带来了RxJava 简洁+异步的优势,也是目前RxJava如此热门的原因所在。
基础概念
说到RxJava,不得不提的就是观察者模式,Android中常用的点击事件,ListView更新,都是观察者模式,大概模式如下:
Button->onClick(View)->OnClickListener
ListView Data Update -> notify -> Adapter
RxJava基于观察者模式, 有如下基本概念:
- Observable (可观察者,即被观察者)
- Observer (观察者)
- subscribe (订阅)、事件
Observable 和 Observer 通过 subscribe() 方法实现订阅关系,从而 Observable 可以在需要的时候发出事件来通知 Observer。支持onNext() ,onCompleted() 和 onError()等回调方法
大概模式如下图:
RxJava基础使用
- 定义Subscriber/Observer
Subscriber/Observer 即观察者,它决定事件触发的时候将有怎样的行为。
Subscriber<String> mySubscriber= new Subscriber<String>() {
@Override
public void onCompleted() {
Log.d(TAG,"onCompleted");
}
@Override
public void onError(Throwable throwable) {
Log.e(TAG,"onError"+throwable.toString());
}
@Override
public void onNext(String s) {
Log.d(TAG,"onNext,s = " + s);
}
}
也可以用Observer,Observer相对Subscriber少了onStart()和unsubscribe()两种方法
2.定义Observable
Observable,它决定什么时候触发事件以及触发怎样的事件。
Observable myObservable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("welcome to");
subscriber.onNext("Saberhao's");
subscriber.onNext("Blog");
subscriber.onCompleted();
}
});
当然,也可以使用just, from等关键字实现相同的功能
Observable<String> myObservable = myObservable.just("welcome to","Saberhao's","Blog")
或者
String[] words = {"welcome to","Saberhao's","Blog"};
Observable myObservable = myObservable .from(words);
3 订阅
mySubscriber.subscribe(myObservable);
实现的核心代码:
public Subscription subscribe(Subscriber subscriber) {
subscriber.onStart();
onSubscribe.call(subscriber);
return subscriber;
}
可以看到,subscriber() 做了3件事:
- 调用 Subscriber.onStart() 。
- 调用 Observable 中的OnSubscribe.call(Subscriber) 。在这里,事件发送的逻辑开始运行。从这也可以看出,在 RxJava 中,Observable 并不是在创建的时候就立即开始发送事件,而是在它被订阅的时候,即当 subscribe() 方法执行的时候。
- 将传入的 Subscriber 作为 Subscription 返回。这是为了方便 unsubscribe().
一旦订阅激活,就会开始打印字符串,效果如下:
Subscribe() 还支持不完整定义的回调,RxJava根据定义创建出Subscriber,上面的等价代码如下
Action1<String> onNextAction = new Action1<String>() {
@Override
public void call(String s) {
Log.d(TAG, "onNext,s = " + s);
}
};
Action1<Throwable> onErrorAction = new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
Log.e(TAG, "onError" + throwable.toString());
}
};
Action0 onCompleteAction = new Action0() {
@Override
public void call() {
Log.d(TAG,"onCompleted");
}
};
Observable myObservable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("welcome to");
subscriber.onNext("Saberhao's");
subscriber.onNext("Blog");
subscriber.onCompleted();
}
});
// 自动创建 Subscriber ,并使用 onNextAction 来定义 onNext()
myObservable.subscribe(onNextAction);
// 自动创建 Subscriber ,并使用 onNextAction 和 onErrorAction 来定义 onNext() 和 onError()
myObservable.subscribe(onNextAction, onErrorAction);
// 自动创建 Subscriber ,并使用 onNextAction、 onErrorAction 和 onCompletedAction 来定义 onNext()、 onError() 和 onCompleted()
myObservable.subscribe(onNextAction, onErrorAction, onCompleteAction);
ActionX 是 RxJava 的一个接口,它只有一个方法 call(),X代表参数个数,0就是没有参数,1就是一个,以此类推,ActionX相当于一个包装对象,将自己的返回值传入对应的subscriber()方法中。
使得RxJava变得灵活,主要有两点: 变换 以及 线程控制,下面我们先通过介绍操作符来看看RxJava是如何变换的
Operate操作符
map()
map的意思就是"映射",也就是把不同类型,或者同一类型的不同数据进行变换,其实 本质就是数据的处理,嗯,我是这样理解的,举个简单的栗子
假如我有一堆图片的URL, 我现在想通过这些URL获取图片,那么我就可以用到map啦
void showMapUse(){
String[] mImageUrls = ...;
Subscriber<String> subscriber = new Subscriber<String>() {
@Override
public void onCompleted() {
}
...
};
Observable.from(mImageUrls)
.map(new Func1<String, Bitmap>() {
@Override
public Bitmap call(String mImageUrl) {
return getBitmapFromUrl(mImageUrl);
}
})
.subscribe(subscriber);
}
这里就华丽丽的实现了从 Stirng 到 Bitmap的类型转换,其实也就是对URL做了处理
对了,好像有什么漏了,对了 这里用到了Func1,其实Func1,FuncX和ActionX都是RxJava封装好的接口,X代表参数个数,他们的区别就在于FuncX有返回值,而ActionX没有。
flatMap()
flatMap意思就是“铺平”+ “映射”,需要“铺平”很明显就是一个集合,下面我们就举一个关于集合的栗子,来看看flatMap怎么使用。
话说中秋到了,公司要给每个员工发中秋礼品,每个人都可以得到若干份,现在请你帮忙打印大家挑选好的礼物(好吧,这个栗子我是想了好久才想到的,原谅我一开始没铺垫好)
先看不用flatMap的实现:
Staff[] staffs = ...;
Subscriber<Staff> subscriber = new Subscriber<Staff>() {
@Override
public void onNext(Staff staff) {
List<Gift> gift = staff.getGift();
for (int i = 0; i < gift.size(); i++) {
Course course = gift.get(i);
Log.d(TAG, course.gift());
}
}
...
};
Observable.from(staffs)
.subscribe(subscriber);
这里有什么问题呢,如果员工的礼物需要到数据库提取,那样放到subscriber()中执行就显得不合适,而且这样写代码也不够简洁,来看看我们用flatMap是怎么实现的
Staff[] staffs = ...;
Subscriber<Gift> subscriber = new Subscriber<Gift>() {
@Override
public void onNext(Gift gift) {
Log.d(TAG, gift.getName());
}
...
};
Observable.from(staffs)
.flatMap(new Func1<Staff, Observable<Gift>>() {
@Override
public Observable<Gift> call(Staff staff) {
return Observable.from(staff.getGift());
}
})
.subscribe(subscriber);
实际上很好理解,就是把原来staffs的Observable “铺平”成子Observable, 然后把子Observable在进行“映射”,最后交给subscriber同一订阅处理,流程如下图:
lift()
无论是Map还是flatMap,本质上都是针对事件序列的处理和再发送,在RxJava中,他们都是基于lift() 变换方法。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);
}
});
}
简单介绍一下,总体上来讲,就是根据operator,通过lift(),来生成一个Observable对象,
这个新的observable对象订阅时,会调用到call方法,call方法主要实现三个功能,也就上面三句代码:
- 根据operator 来生成一个新的subscriber
- 启动subcriber
- 也是最关键的一步,将新的subscriber 传递给新的observable
通过上面三步,就完成了一个订阅事件的封装和传递,总体来说,有点像代理模式,原有的observable以为自己启动了订阅内容,其实他是通过new observable实现的。
流程如下:
灵活使用lift(),你就可以定义自己的转换操作咯,下面是从参考文章 摘录来的类型转换的例子,虽然没什么实际意义,不过留下来以后可以参考
observable.lift(new Observable.Operator<String, Integer>() {
@Override
public Subscriber<? super Integer> call(final Subscriber<? super String> subscriber) {
// 将事件序列中的 Integer 对象转换为 String 对象
return new Subscriber<Integer>() {
@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()
Compose()和 lift() 的区别在于, lift() 是针对事件项和事件序列的(针对内部),而 compose() 是针对 Observable 自身进行变换(针对observable所出环境)。举个例子,(http://gank.io/post/560e15be2dca930e00da1083#toc_22) 假设在程序中有多个 Observable ,并且他们都需要应用一组相同的变换集合。你可以这么写:
private Observable changeAll(Observable observable) {
return observable
.map()
.flatMap()
.lift();
}
...
changeAll(observable1).subscribe(subscriber1);
changeAll(observable2).subscribe(subscriber2);
changeAll(observable3).subscribe(subscriber3);
这个时候observable 被包含在changeAll方法中,我们要让observable解放出来,让他更符合RxJava的灵活性,可以使用Compose() :
public class ChangeAllTransformer implements Observable.Transformer<Integer, String> {
@Override
public Observable<String> call(Observable<Integer> observable) {
return observable
.map()
.flatMap()
.lift();
}
}
...
Transformer changeAll = new ChangeAllTransformer();
observable1.compose(changeAllTransformer).subscribe(subscriber1);
observable2.compose(changeAllTransformer).subscribe(subscriber2);
observable3.compose(changeAllTransformer).subscribe(subscriber3);
其他常见操作
比较常用的操作符还有 filter(),take(),doOnNext(), 分别用于过滤,指定输出结果数量,以及在每次输出前做些特定操作。
接着上面月饼的例子,如果月饼是必备的,所以我只查询不是月饼的礼物,且只想知道其中的两种礼物(¯﹃¯),同时我想把这些礼物名单保留下来,我该怎么做?
Staff[] staffs = ...;
Subscriber<Gift> subscriber = new Subscriber<Gift>() {
@Override
public void onNext(Gift gift) {
Log.d(TAG, gift.getName());
}
...
};
Observable.from(staffs)
.flatMap(new Func1<Staff, Observable<Gift>>() {
@Override
public Observable<Gift> call(Staff staff) {
return Observable.from(staff.getGift());
}
})
.filter((new Func1<gift, Boolean>() {
@Override
public Boolean call(Gift gift) {
return !gift.getName().equals("月饼");
}
})
.take(2)
.doOnNext(new Action1<Gift>() {
@Override
public void call(Gift gift) {
saveGiftName(gift.getName);
}
});
.subscribe(subscriber);
doOnNext虽然名字和SubScribe().onNext差不多,但是在线程处理上没必然联系,具体请自行谷歌~ RxJava还有许多操作符,后面会慢慢收集丰富~
线程控制Scheduler
写到这里 ,以及好累了,不分篇就是有这种不好的地方,开篇说过,RxJava的特定就是简洁+异步,RxJava使用调度器Scheduler进行线程切换,极大满足了用户异步操作的需求,真正做到,后台处理数据,前台显示!
基本用法
subscribeOn : 指定订阅响应动作所在线程,只能指定一次
observeOn():指定在它之后的操作所在的线程,可以不断切换
一般的用法:
Observable.from(staffs) // IO 线程,由 subscribeOn() 指定
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.flatMap(flatMapOperator) // 新线程,由 observeOn() 指定
.observeOn(Schedulers.io())
.map(mapOperator) // IO 线程,由 observeOn() 指定
线程切换进阶
为啥说subscribeOn只能指定一次,其实很好理解,就是订阅的响应只有一个,所以只认一次切换,当然比较高冷的说法,可以从原理来分析。
subscribeOn()原理:
observeOn() 原理:
图中可以看出,subscribeOn() 和 observeOn() 都做了线程切换的工作(图中的 “schedule…” 部位,仔细找可以看到,反正我也是找了很久),subscribeOn() 的线程切换发生在 OnSubscribe 中,在没有通知上一级时,这个线程还是有可能被切换。而 observeOn() 的线程切换则发生在它内建的 Subscriber 中,即发生在它即将给下一级 Subscriber 发送事件时,因此 observeOn() 控制的是它后面的线程。
结合下面的图片,就可以很好理解这些概念了:
从图我们可以了解到:
- subscribeOn只认一次切换
- subscribeOn只认第一次切换
防止内存泄露
在 subscribe() 之后, Observable 会持有 Subscriber 的引用,这个引用如果不能及时被释放,将有内存泄露的风险。所以最好保持一个原则:要在不再使用的时候尽快在合适的地方(例如 onPause() onStop() 等方法中)调用 unsubscribe() 来解除引用关系,以避免内存泄露的发生。
Subscription 类只有两个方法,unsubscribe() 和 isUnsubscribed()。通常在需要取消订阅的地方,先查询是否订阅 而后取消订阅,这个步骤是固定的,如果你正在处理多个 Observables 和 Subscribers,可以将他们添加到CompositeSubscription中,,然后可以使用 CompositeSubscription.unsubscribe() 方法在同一时间进行退订(unsubscribed)。
public void onUnsubscribe() {
if (compositeSubscription != null&&compositeSubscription.hasSubscriptions()) {
compositeSubscription.unsubscribe();
}
}
后记
虽然参考文章写的非常好,但是自己动笔才发现许多模糊的地方,写完本文后理清不少,以后还是勤快点更新博客,第一次用MD写博客,体验还不错~