Rx并不是一种新的语言,而是一种普通的Java模式,类似于观察者模式(Observer Pattern),可以将它看作一个普通的Java类库,因此你可以立即使用RxJava。而RxAndroid是RxJava的一个针对Android平台的扩展,主要用于 Android 开发
- API 介绍和原理简析
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() 二者也是互斥的,即在队列中调用了其中一个,就不应该再调用另一个。 - 基本实现
1) 创建观察者Observer
Observer 即观察者,它决定事件触发的时候将有怎样的行为。 RxJava 中的 Observer 接口的实现方式:
Observer<String > observer = new Observer<String>() {
@Override
public void onNext(String s) {
Log.e(tag, "Item: " + s);
}
@Override
public void onCompleted() {
Log.e(tag, "Completed!");
}
@Override
public void onError(Throwable e) {
Log.e(tag, "Error!");
}
};
除了 Observer 接口之外,RxJava 还内置了一个实现了 Observer 的抽象类:Subscriber。 Subscriber 对 Observer 接口进行了一些扩展,但他们的基本使用方式是完全一样的:
Subscriber<String> subscriber = new Subscriber<String>() {
@Override
public void onNext(String s) {
Log.e(tag, "Item: " + s);
}
@Override
public void onCompleted() {
Log.e(tag, "Completed!");
}
@Override
public void onError(Throwable e) {
Log.e(tag, "Error!");
}
};
Observer 和 Subscriber它们的区别对于使用者来说主要有两点:
onStart(): 这是 Subscriber 增加的方法。它会在 subscribe 刚开始,而事件还未发送之前被调用,可以用于做一些准备工作,例如数据的清零或重置。这是一个可选方法,默认情况下它的实现为空。需要注意的是,如果对准备工作的线程有要求(例如弹出一个显示进度的对话框,这必须在主线程执行), onStart() 就不适用了,因为它总是在 subscribe 所发生的线程被调用,而不能指定线程。要在指定的线程来做准备工作,可以使用 doOnSubscribe() 方法,具体可以在后面的文中看到。
unsubscribe(): 这是 Subscriber 所实现的另一个接口 Subscription 的方法,用于取消订阅。在这个方法被调用后,Subscriber 将不再接收事件。一般在这个方法调用前,可以使用 isUnsubscribed() 先判断一下状态。 unsubscribe() 这个方法很重要,因为在 subscribe() 之后, Observable 会持有 Subscriber 的引用,这个引用如果不能及时被释放,将有内存泄露的风险。所以最好保持一个原则:要在不再使用的时候尽快在合适的地方(例如 onPause() onStop() 等方法中)调用 unsubscribe() 来解除引用关系,以避免内存泄露的发生。
2) 创建 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();
}
});
Observable observable = Observable.just("Hello","Hi","Aloha"); 将传入的参数依次发送出来。
List<String> list = new ArrayList();
list.add( "1" ) ;
list.add( "2" ) ;
list.add( "3" ) ;
Observable observable = Observable.from(list);
from(T[]) / from(Iterable<? extends T>) : 将传入的数组或 Iterable 拆分成具体对象后,依次发送出来。
3) Subscribe (订阅)
创建了 Observable 和 Observer 之后,再用 subscribe() 方法将它们联结起来,整条链子就可以工作了。代码形式很简单:
observable.subscribe(observer);
// 或者:
observable.subscribe(subscriber);
3 线程控制 —— Scheduler (一)
在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。
AndroidSchedulers.mainThread(),它指定的操作将在 Android 主线程运行。
有了这几个 Scheduler ,就可以使用 subscribeOn() 和 observeOn() 两个方法来对线程进行控制了。 * subscribeOn(): 指定 subscribe() 所发生的线程,即 Observable.OnSubscribe 被激活时所处的线程。或者叫做事件产生的线程。 * observeOn(): 指定 Subscriber 所运行在的线程。或者叫做事件消费的线程。
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.e(tag, "number:" + number);
}
});
4 变换
所谓变换,就是将事件序列中的对象或整个序列进行加工处理,转换成不同的事件或事件序列
map(): 事件对象的直接变换,它是 RxJava 最常用的变换。
map()使用
Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
Log.e( "rx_call" , Thread.currentThread().getName() );
subscriber.onNext( "dd");
subscriber.onCompleted();
}
})
.observeOn(Schedulers.newThread()) //新线程
.map(new Func1<String, String >() {
@Override
public String call(String s) {
Log.e( "rx_map" , Thread.currentThread().getName() );
return s + "88";
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
Log.e( "rx_subscribe" , Thread.currentThread().getName());
}
}) ;
flatMap(): 这是一个很有用但非常难理解的变换,flatMap() 中返回的是个 Observable 对象,并且这个 Observable 对象并不是被直接发送到了 Subscriber 的回调方法中
flatMap() 的原理是这样的:1. 使用传入的事件对象创建一个 Observable 对象;2. 并不发送这个 Observable, 而是将它激活,于是它开始发送事件;3. 每一个创建出来的 Observable 发送的事件,都被汇入同一个 Observable ,而这个 Observable 负责将这些事件统一交给 Subscriber 的回调方法
flatMap()使用
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);
2) 变换的原理:lift()
在 Observable 执行了 lift(Operator) 方法之后,会返回一个新的 Observable,这个新的 Observable 会像一个代理一样,负责接收原始的 Observable 发出的事件,并在处理后发送给 Subscriber。
3) compose: 对 Observable 整体的变换
它和 lift() 的区别在于, lift() 是针对事件项和事件序列的,而 compose() 是针对 Observable 自身进行变换
Observable.just("123")
.compose(RxUtil.<String>applySchedulers())
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
Log.e("call---subscribe ",Thread.currentThread().getName() +";内容="+s);
}
});
5 线程控制:Scheduler (二)
操作符(Operators):操作符就是为了解决对Observable对象的变换的问题
1.Observable和Subscriber可以做任何事情
Observable可以是一个数据库查询,Subscriber用来显示查询结果;Observable可以是屏幕上的点击事件,Subscriber用来响应点击事件;Observable可以是一个网络请求,Subscriber用来显示请求结果。
2.Observable和Subscriber是独立于中间的变换过程的。
在Observable和Subscriber中间可以增减任何数量的map。整个系统是高度可组合的,操作数据是一个很简单的过程。
filter()输出和输入相同的元素,并且会过滤掉那些不满足检查条件的。
take()输出最多指定数量的结果
doOnNext()允许我们在每次输出一个元素之前做一些额外的事情,比如这里的保存标题
subscribeOn(): 指定 subscribe() 所发生的线程,即 Observable.OnSubscribe 被激活时所处的线程。或者叫做事件产生的线程。
observeOn(): 指定 Subscriber 所运行在的线程。或者叫做事件消费的线程。
使用Observable.fromCallable()方法有两点好处:
获取要发送的数据的代码只会在有Observer订阅之后执行。
获取数据的代码可以在子线程中执行。
zip 操作符,合并多个观察对象的数据。并且允许 Func2()函数重新发送合并后的数据
scan累加器操作符的使用
filter 过滤操作符的使用
take :取前n个数据
takeLast:取后n个数据
first 只发送第一个数据
last 只发送最后一个数据
skip() 跳过前n个数据发送后面的数据
skipLast() 跳过最后n个数据,发送前面的数据
elementAt 、elementAtOrDefault
elementAt() 发送数据序列中第n个数据 ,序列号从0开始
如果该序号大于数据序列中的最大序列号,则会抛出异常,程序崩溃
所以在用elementAt操作符的时候,要注意判断发送的数据序列号是否越界
elementAtOrDefault( int n , Object default ) 发送数据序列中第n个数据 ,序列号从0开始。
如果序列中没有该序列号,则发送默认值
startWith() 插入数据
delay操作符,延迟数据发送
Timer 延时操作符的使用
interval 轮询操作符,循环发送数据,数据从0开始递增
doOnNext() 操作符,在每次 OnNext() 方法被调用前执行
使用场景:从网络请求数据,在数据被展示前,缓存到本地
12、Buffer 操作符
Buffer( int n ) 把n个数据打成一个list包,然后再次发送。
Buffer( int n , int skip) 把n个数据打成一个list包,然后跳过第skip个数据。
throttleFirst 操作符
使用场景:1、button按钮防抖操作,防连续点击 2、百度关键词联想,在一段时间内只联想一次,防止频繁请求服务器
distinct 过滤重复的数据
distinctUntilChanged() 过滤连续重复的数据
debounce() 操作符
一段时间内没有变化,就会发送一个数据。
doOnSubscribe()
使用场景: 可以在事件发出之前做一些初始化的工作,比如弹出进度条等等
range 操作符的使用
defer 操作符
just操作符是在创建Observable就进行了赋值操作,而defer是在订阅者订阅时才创建Observable,此时才进行真正的赋值操作。
.subscribeOn(Schedulers.io()) 和 .observeOn(AndroidSchedulers.mainThread()) 写的位置不一样,造成的结果也不一样。从例4中可以看出 map() 操作符默认运行在事件产生的线程之中。事件消费只是在 subscribe() 里面。
对于 create() , just() , from() 等 --- 事件产生
map() , flapMap() , scan() , filter() 等 -- 事件加工
subscribe() -- 事件消费
事件产生:默认运行在当前线程,可以由 subscribeOn() 自定义线程
事件加工:默认跟事件产生的线程保持一致, 可以由 observeOn() 自定义线程
事件消费:默认运行在当前线程,可以有observeOn() 自定义
如果只规定了事件产生的线程,那么事件消费线程将跟随事件产生线程。
如果只规定了事件消费的线程,那么事件产生的线程和 当前线程保持一致
6 Retrofit的使用就是以下几步:
定义接口,参数声明,Url都通过Annotation指定
通过RestAdapter生成一个接口的实现类(动态代理)
调用接口请求数据
创建Retrofit 实例:
public static final String BASE_URL = "http://api.myservice.com";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
定义 Endpoints:interface 用下面的方式定义了每个endpoint
public interface MyApiEndpointInterface {
// Request method and URL specified in the annotation
// Callback for the parsed response is the last parameter
@GET("/users/{username}")
Call<User> getUser(@Path("username") String username);
@GET("/group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
@POST("/users/new")
Call<User> createUser(@Body User user);
}
Multipart forms:
如果你要提交多参数表单数据(multi-part form data),可以使用@Multipart与@Part注解:
String username = "sarahjean";
Call<User> call = apiService.getUser(username);
call.enqueue(new Callback<User>() {
@Override
public void onResponse(Response<User> response) {
int statusCode = response.code();
User user = response.body();
}
@Override
public void onFailure(Throwable t) {
// Log error here since request failed
}
});