最近公司项目使用了rxjava,学习了之后感觉真的很很厉害。RxJava 在 GitHub 主页上的自我介绍是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM",意思是“一个可以在JVM上使用可观察序列来组成异步的和基于事件的程序的库”。在学习之前先了解了解几个单词的字面含义,不然看到后面太晕了。
observable 可观察者,即被观察者n
observer 观察者n
observe 观察v
subscriber 订阅者n
subscribe 订阅v
scheduler 调度程序n
我理解rxjava最基本也是最普通的用法就是:observable.subscribe(subscriber);翻成中文就是“被观察者.subscribe(订阅者)”;
在被观察者做的操作处理的事情在订阅发生后都是要“告诉”或者说传递给订阅者,然后订阅者里再执行回调代码。
基本用法
第一步:我们可以先创建一个被观察者
//创建一个被订阅者 creat
final Observable observable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("沐小奶:闪光");
subscriber.onNext("沐小奶他哥:老子跑~");
subscriber.onNext("沐小奶:束缚");
subscriber.onNext("沐小奶他哥:切弓射~");
subscriber.onNext("沐小奶:痛苦锁链");
subscriber.onNext("沐小奶他哥:黄药,准备30s后解掉痛苦,结果~~");
subscriber.onNext("沐小奶:暴风祈祷,霹雳!!暴击!!!");
subscriber.onNext("沐小奶他哥:你麻痹!");
subscriber.onNext("沐小奶他哥,卒!");
subscriber.onCompleted();
}
});
而且创建observable的方法有很多,官方文档里提供的有这些
just( ) — 将一个或多个对象转换成发射这个或这些对象的一个Observable
from( ) — 将一个Iterable, 一个Future, 或者一个数组转换成一个Observable
repeat( ) — 创建一个重复发射指定数据或数据序列的Observable
repeatWhen( ) — 创建一个重复发射指定数据或数据序列的Observable,它依赖于另一个Observable发射的数据
create( ) — 使用一个函数从头创建一个Observable
defer( ) — 只有当订阅者订阅才创建Observable;为每个订阅创建一个新的Observable
range( ) — 创建一个发射指定范围的整数序列的Observable
interval( ) — 创建一个按照给定的时间间隔发射整数序列的Observable
timer( ) — 创建一个在给定的延时之后发射单个数据的Observable
empty( ) — 创建一个什么都不做直接通知完成的Observable
error( ) — 创建一个什么都不做直接通知错误的Observable
never( ) — 创建一个不发射任何数据的Observable
再列举一下just 和from 的用法
/**
* 创建一个被订阅者 just
* just(T...): 将传入的参数依次发送出来。
* onNext("沐小奶:闪光");
* onNext("沐小奶他哥:老子跑~");
* onCompleted();
* 将会依次调用:
*/
final Observable observable = Observable.just(
"沐小奶:闪光",
"沐小奶他哥:老子跑~"
);
就如说明里显示的那样 ,他内部实际已经依次发射出“沐小奶:闪光”,“沐小奶他哥;老子跑~”和creat感觉意思是一样的
再看看from的
/**
* 创建一个被订阅者 from
* from(T[]) / from(Iterable<? extends T>) : 将传入的数组或 Iterable 拆分成具体对象后,依次发送出来。
* 将会依次调用:
onNext("弈剑听雨");
onNext("秀川");
onNext("张小凡");
onNext("鬼厉");
onNext("天问");
onCompleted();
*/
String[] names = {"弈剑听雨", "秀川", "张小凡", "鬼厉", "天问"};
final Observable observable = Observable.from(names);
都是一样的。
第二步 :创建一个订阅者
//创建一个订阅者
final Subscriber<String> subscriber = new Subscriber<String>() {
@Override
public void onCompleted() {
// 订阅事件被触发 并完成
}
@Override
public void onError(Throwable e) {
// 订阅事件触发后 出现错误
}
@Override
public void onNext(String s) {
//订阅事件发出时 所执行的回调代码
XgoLog.i("======== " + s);
Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();
}
};
事实上,subscribe()还支持不完整定义的回调,于是可以看看下面的例子
/**
* Action1 也是一个接口,它同样只有一个方法call(T param),这个方法也无返回值,但有一个参数;
* 与 Action0 同理,由于 onNext(T obj) 和 onError(Throwable error)也是单参数无返回值的,
* 因此 Action1 可以将 onNext(obj) 和 onError(error) 打包起来传入 subscribe() 以实现不完整定义的<span style="white-space:pre"> </span> 回调。
* 事实上,虽然 Action0 和 Action1 在 API 中使用最广泛,但 RxJava 是提供了多个 ActionX 形式的接口
* (例如Action2, Action3) 的,它们可以被用以包装不同的无返回值的方法。
*/
final Action1<String> onNextAction = new Action1<String>() {
@Override
public void call(String s) {
XgoLog.i("======== " + s);
Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();
}
};
final Action1<Throwable> onErrorAction = new Action1<Throwable>() {
@Override
public void call(Throwable s) {
}
};
/**
* Action0 是 RxJava 的一个接口,它只有一个方法 call(),这个方法是无参无返回值的;
* 由于 onCompleted() 方法也是无参无返回值的,因此 Action0 可以被当成一个包装对象,
* 将 onCompleted()的内容打包起来将自己作为一个参数传入 subscribe() 以实现不完整定义的回调。
* 这样其实也可以看做将 onCompleted() 方法作为参数传进了 subscribe(),相当于其他某些语言中的『闭包』。
*/
final Action0 onCompletedAction = new Action0() {
@Override
public void call() {
XgoLog.i("======== completed");
}
};
值得注意的是 Action0,
Action1 方法,后面还有ActionX等,上面的注释解释的很清楚了, 不严谨的我梦可以简单的理解Action0折方法无参无返回值,Action1 一个参无返回值这样。
第三步:订阅
<span style="white-space:pre"> </span> observable.subscribe(subscriber);
// observable.subscribe(onNextAction, onErrorAction, onCompletedAction);
// observable.subscribe(onNextAction, onErrorAction);
// observable.subscribe(onNextAction);
这样一次基本的Rxjava使用就完成了。
线程
/**
* 由 id 取得图片并显示
* 由指定的一个drawable文件id drawableRes取得图片,并显示在ImageView中,并在出现异常的时候打印 Toast报错
* Schedulers.immediate(): 直接在当前线程运行,相当于不指定线程。这是默认的 Scheduler。
* Schedulers.newThread(): 总是启用新线程,并在新线程执行操作。
* Schedulers.io(): I/O 操作(读写文件、读写数据库、网络信息交互等)所使用的 Scheduler。行为模式和 newTh read() 差不多,区别在于 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() 两个方法来对线程进行控制了。
* <p/>
* subscribeOn(): 指定 subscribe() 所发生的线程,即 Observable.OnSubscribe 被激活时所处的线程。或者叫 做事件产生的线程。
* observeOn(): 指定 Subscriber 所运行在的线程。或者叫做事件消费的线程。
*/
private void demo3() {
final ImageView imageView = (ImageView) findViewById(R.id.iv_drawable);
Observable.create(new Observable.OnSubscribe<Drawable>() {
@Override
public void call(Subscriber<? super Drawable> subscriber) {
Drawable drawable = getResources().getDrawable(R.drawable.act_splash);
subscriber.onNext(drawable);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程
.subscribe(new Subscriber<Drawable>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Toast.makeText(MainActivity.this, "ERROR!!", Toast.LENGTH_SHORT).show();
}
@Override
public void onNext(Drawable drawable) {
imageView.setImageDrawable(drawable);
}
});
}
注释里已经大致解说了一下scheduler 相关的api,除此 Android 还有一个专用的
AndroidSchedulers.mainThread()
,它指定的操作将在 Android 主线程运行。
有了这几个 scheduler
,就可以使用
subscribeOn()
和
observeOn()
两个方法来对线程进行控制了。
subscribeOn()
: 指定
subscribe()
所发生的线程,即
Observable.OnSubscribe
被激活时所处的线程。或者叫做事件产生的线程。
observeOn()
: 指定
Subscriber
所运行在的线程。或者叫做事件消费的线程。
subscribeOn(Schedulers.io())
的指定,被创建的事件的内容drawable加载图片
将会在 IO 线程发出;而由于observeOn(AndroidScheculers.mainThread()
) 的指定,因此 subscriber
图片的显示将发生在主线程 。事实上,这种在subscribe()
之前写上两句 subscribeOn(Scheduler.io())
和 observeOn(AndroidSchedulers.mainThread())
的使用方式非常常见,它适用于多数的 『后台线程取数据,主线程显示』的程序策略。subscribeOn()
结合 observeOn()
来实现线程控制,让事件的产生和消费发生在不同的线程。那么可不可以多切换几次线程呢?可以的。
observeOn()
指定的是 Subscriber
的线程,而这个 Subscriber
并不一定是subscribe()
参数中的 Subscriber
,而是 observeOn()
执行时的当前 Observable
所对应的Subscriber
,即它的直接下级 Subscriber
。换句话说,observeOn()
指定的是它之后的操作所在的线程。因此如果有多次切换线程的需求,只要在每个想要切换线程的位置调用一次 observeOn()
即可。observeOn()
的多次调用,程序实现了线程的多次切换。不过,不同于 observeOn()
, subscribeOn()
的位置放在哪里都可以,但它是只能调用一次的。why?subscribeOn()
和 observeOn()
都做了线程切换的工作(图中的 "schedule" 部位)。不同的是,subscribeOn()
的线程切换发生在 OnSubscribe
中,即在它通知上一级 OnSubscribe
时,这时事件还没有开始发送,因此subscribeOn()
的线程控制可以从事件发出的开端就造成影响;而 observeOn()
的线程切换则发生在它内建的 Subscriber
中,即发生在它即将给下一级 Subscriber
发送事件时,因此 observeOn()
控制的是它后面的线程。
doOnSubscribe()
有一个方法 Observable.doOnSubscribe()
,是在subscribe()
调用后而且在事件发送前执行,它可以指定线程。默认情况下, doOnSubscribe()
执行在 subscribe()
发生的线程;而如果在 doOnSubscribe()
之后有 subscribeOn()
的话,它将执行在离它最近的 subscribeOn()
所指定的线程。
Observable.just(“吸血波”, “地震波”, “枯竭波”, “压力波”) // 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() 指定
int drawableRes = R.mipmap.malin;
Observable.create(new Observable.OnSubscribe<Drawable>() { //1:被观察者
@Override
public void call(Subscriber<? super Drawable> subscriber) {
Logger.d("被观察者");
Drawable drawable = getResources().getDrawable(drawableRes);
subscriber.onNext(drawable);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io())//事件产生的线程。指定 subscribe() 发生在 IO 线程
// doOnSubscribe() 之后有 subscribeOn() 的话,它将执行在离它最近的 subscribeOn() 所指定的线程。这里将执行在主线程中
.doOnSubscribe(new Action0() {
@Override
public void call() {
if (mProgressBar != null) {
mProgressBar.setVisibility(View.VISIBLE);//显示一个等待的ProgressBar--需要在主线程中执行
}
}
})
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())//指定 Subscriber 所运行在的线程。或者叫做事件消费的线程
.subscribe(new Subscriber<Drawable>() { //3:订阅 //2:观察者
@Override
public void onCompleted() {
if (mProgressBar!=null){
mProgressBar.setVisibility(View.GONE);
}
Logger.d("观察者 onCompleted()");
Toast.makeText(MainActivity.this, "观察者 onCompleted()", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(Throwable e) {
if (mProgressBar!=null){
mProgressBar.setVisibility(View.GONE);
}
Logger.d("观察者 onError()");
Toast.makeText(MainActivity.this, "观察者 onError() " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onNext(Drawable drawable) {
Toast.makeText(MainActivity.this, "观察者 onNext()", Toast.LENGTH_SHORT).show();
Logger.d("观察者 onNext()");
mImageView.setImageDrawable(drawable);
}
});
变换
private void demo5() {
Observable.from(DataFactory.getData())
.map(new Func1<Student, String>() {
@Override
public String call(Student student) {
return student.name;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<String>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(String s) {
XgoLog.i("=======观察者: " + s);
// 日志
// 03-17 17:47:46.852 13680-13680/? I/xgo: =======观察者: 学生1
// 03-17 17:47:46.852 13680-13680/? I/xgo: =======观察者: 学生2
// 03-17 17:47:46.852 13680-13680/? I/xgo: =======观察者: 学生3
}
});
}
private void demo6() {
Observable.from(DataFactory.getData())
.map(new Func1<Student, String>() {
@Override
public String call(Student student) {
return student.name;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
XgoLog.i("=======观察者: " + s);
// 日志
// 03-17 17:47:46.852 13680-13680/? I/xgo: =======观察者: 学生1
// 03-17 17:47:46.852 13680-13680/? I/xgo: =======观察者: 学生2
// 03-17 17:47:46.852 13680-13680/? I/xgo: =======观察者: 学生3
}
});
}
demo5() 和demo6() 是一样的。这又可以看到subscribe()支持不完整定义。
flatMap()变换
/**
* demo7()
* (在observable里创建的被观察者是什么或者最终经过map()或者flatMap()变换之后的是什么类型 在观察者里的数据就是什么类型)
* 需要:输出每一个学生所有选修的课程
* 输出每一个学生选修的课程
*/
private void demo7(){
Observable.from(DataFactory.getData())
.map(new Func1<Student, ArrayList<Course>>() {
@Override
public ArrayList<Course> call(Student student) {
return student.courses;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<ArrayList<Course>>() {
@Override
public void call(ArrayList<Course> courses) {
for (int i=0;i<courses.size();i++){
XgoLog.i("=======观察者: " + courses.get(i).name);
// 03-17 18:22:35.102 15300-15300/? I/xgo: =======观察者: 学生1的课程1
// 03-17 18:22:35.102 15300-15300/? I/xgo: =======观察者: 学生1的课程2
// 03-17 18:22:35.102 15300-15300/? I/xgo: =======观察者: 学生2的课程1
// 03-17 18:22:35.102 15300-15300/? I/xgo: =======观察者: 学生2的课程2
// 03-17 18:22:35.102 15300-15300/? I/xgo: =======观察者: 学生3的课程1
// 03-17 18:22:35.102 15300-15300/? I/xgo: =======观察者: 学生3的课程2
}
}
});
}
/**
* demo8()
* 引入flatMap()
* 需要:输出每一个学生所有选修的课程
* 输出每一个学生选修的课程
*/
private void demo8(){
Observable.from(DataFactory.getData())
.flatMap(new Func1<Student, Observable<Course>>() {
@Override
public Observable<Course> call(Student student) {
return Observable.from(student.courses);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Course>() {
@Override
public void call(Course course) {
XgoLog.i("=======观察者: " + course.name);
// 03-17 18:30:00.552 16060-16060/? I/xgo: =======观察者: 学生1的课程1
// 03-17 18:30:00.552 16060-16060/? I/xgo: =======观察者: 学生1的课程2
// 03-17 18:30:00.552 16060-16060/? I/xgo: =======观察者: 学生2的课程1
// 03-17 18:30:00.552 16060-16060/? I/xgo: =======观察者: 学生2的课程2
// 03-17 18:30:00.552 16060-16060/? I/xgo: =======观察者: 学生3的课程1
// 03-17 18:30:00.552 16060-16060/? I/xgo: =======观察者: 学生3的课程2
}
});
}
认真对比demo7()和demo8()发现flatmap 里面是有一个observerable再来一次,感觉也是找到里面的嵌套循环,但是把数据扁平化而非嵌套的形式挖出来的,就好比一个是“人”字型,一个是“一”字型。
使用
/**
* 防抖动
*/
RxView.clicks(mButton1)
.throttleFirst(1000, TimeUnit.MILLISECONDS)//500ms,第一次点击后,500ms内点击无效,500ms后点击才会响应
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Void>() {
@Override
public void call(Void aVoid) {
XgoLog.i("======== 你点的太快了~");
Toast.makeText(MainActivity.this, "你点的太快了~", Toast.LENGTH_SHORT).show();
}}
);
和retrofit的结合
/**
* 获取用户评论 v1.2
*
* @param user_id 用户ObjectId
* @param last 获取下一页数据的条件.如果为空,则从第一条开始查询
* @param head feed流第一条数据条件
* @param status 评论类型
* @param access_token token
*/
@GET("gaschat/user/{id}/comments")
Observable<LTUserComents> getUserAllComments(@Path("id") String user_id,
@Query("last") String last,
@Query("head") String head,
@Query("status") String status,
@Query("access_token") String access_token);
ServiceFactory.creatService(UserService.class)
.getUserAllComments(GCApp.getUserData().id, last, head, "1", TokenKeyStore.getInstance().getToken())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<LTUserComents>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(LTUserComents ltUserComents) {
}
})
公司目前就是这么用的。
当 RxJava 形式的时候,Retrofit 把请求封装进 Observable
,在请求结束后调用 onNext()
或在请求失败后调用 onError()
。
对比来看, Callback
形式和 Observable
形式长得不太一样,但本质都差不多。但是一旦情况复杂,r结合rxjava会更明了。
比如callback嵌套的时候
public void saveTheCutestCat(String query , final CallBack<Uri> cutestCatCallback ){
apiWrapper.queryCats(query).start(new CallBack<List<Cat>>() {
@Override
public void onSuccess(List<Cat> result) {
Cat cat = findCat(result);
apiWrapper.store(cat).start(new CallBack<Uri>() {
@Override
public void onSuccess(Uri result) {
cutestCatCallback.onSuccess(result);
}
@Override
public void onError(Exception e) {
cutestCatCallback.onError(e);
}
});
}
@Override
public void onError(Exception e) {
cutestCatCallback.onError(e);
}
});
}
private Cat findCat(List<Cat> list) {
return Collections.max(list);
}
如果结合rxjava就可以这样
private ApiWrapper apiWrapper;
public Observable<Uri> saveTheCutestCat(String query) {
return apiWrapper.queryCats(query)
.map(new Func1<List<Cat>, Cat>() {
@Override
public Cat call(List<Cat> cats) {
return findCat(cats);
}
}).flatMap(new Func1<Cat, Observable<Uri>>() {
@Override
public Observable<Uri> call(Cat cat) {
return apiWrapper.store(cat);
}
});
}
private Cat findCat(List<Cat> list) {
return Collections.max(list);
}
简单来说就把人字换成了一字。
再假设这么一种情况:你的程序取到的 User
并不应该直接显示,而是需要先与数据库中的数据进行比对和修正后再显示。使用Callback
方式大概可以这么写:
getUser(userId, new Callback<User>() {
@Override
public void success(User user) {
processUser(user); // 尝试修正 User 数据
userView.setUser(user);
}
@Override
public void failure(RetrofitError error) {
// Error handling
...
}
};
但这样做会影响性能。数据库的操作很重,一次读写操作花费 10~20ms 是很常见的,这样的耗时很容易造成界面的卡顿。所以通常情况下,如果可以的话一定要避免在主线程中处理数据库。所以为了提升性能,这段代码可以优化一下:
getUser(userId, new Callback<User>() {
@Override
public void success(User user) {
new Thread() {
@Override
public void run() {
processUser(user); // 尝试修正 User 数据
runOnUiThread(new Runnable() { // 切回 UI 线程
@Override
public void run() {
userView.setUser(user);
}
});
}).start();
}
@Override
public void failure(RetrofitError error) {
// Error handling
...
}
};
但是各种缩进, 如果用 RxJava 的形式就是这样
getUser(userId)
.doOnNext(new Action1<User>() {
@Override
public void call(User user) {
processUser(user);
})
.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
...
}
});
也是变成一字型。