本篇文章已受权微信公众号 guolin_blog (郭霖)独家发布javascript
RxJava究竟是什么?让咱们直接跳过官方那种晦涩的追求精确的定义,其实初学RxJava只要把握两点:观察者模式和异步,就基本能够熟练使用RxJava了。java
异步在这里并不须要作太多的解释,由于在概念和使用上,并无太多高深的东西。大概就是你脑子里想能到的那些多线程,线程切换这些东西。我会在后面会讲解它的用法。git
咱们先把观察者模式说清楚
github
“按下开关,台灯灯亮”微信
在这个事件中,台灯做为观察者,开关做为被观察者,台灯透过电线来观察开关的状态来并作出相应的处理多线程
观察上图,其实已经很明了了,不过须要指出一下几点(对于下面理解RxJava很重要):架构
我必须苦口婆心的告诉你:咱们总结的这三点对于咱们理解RxJava很是重要。由于上述三条分别对应了RxJava中被观察者(Observable),观察者(Observer)和操做符的职能。而观察者模式又是RxJava程序运行的骨架。异步
好了,我假设你已经彻底理解了我上面讲述的东西。咱们正式进入RxJava!ide
RxJava也是基于观察者模式来组建本身的程序逻辑的,就是构建被观察者(Observable),观察者(Observer/Subscriber),而后创建两者的订阅关系(就像那根电线,链接起台灯和开关)实现观察,在事件传递过程当中还能够对事件作各类处理。post
Tips: Observer是观察者的接口, Subscriber是实现这个接口的抽象类,所以两个类均可以被当作观察者,因为Subscriber在Observe的基础上作了一些拓展,加入了新的方法,通常会更加倾向于使用Subscriber。
Observable switcher=Observable.create(new Observable.OnSubscribe<String>(){
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("On");
subscriber.onNext("Off");
subscriber.onNext("On");
subscriber.onNext("On");
subscriber.onCompleted();
}
});复制代码
这是最正宗的写法,建立了一个开关类,产生了五个事件,分别是:开,关,开,开,结束。
Observable switcher=Observable.just("On","Off","On","On");复制代码
String [] kk={"On","Off","On","On"};
Observable switcher=Observable.from(kk);复制代码
偷懒模式是一种简便的写法,实际上也都是被观察者把那些信息"On","Off","On","On",包装成onNext("On")这样的事件依次发给观察者,固然,它本身补上了onComplete()事件。
以上是最经常使用到的建立方式,好了,咱们就建立了一个开关类。
Subscriber light=new Subscriber<String>() {
@Override
public void onCompleted() {
//被观察者的onCompleted()事件会走到这里;
Log.d("DDDDDD","结束观察...\n");
}
@Override
public void onError(Throwable e) {
//出现错误会调用这个方法
}
@Override
public void onNext(String s) {
//处理传过来的onNext事件
Log.d("DDDDD","handle this---"+s)
}复制代码
这也是比较常见的写法,建立了一个台灯类。
Action1 light=new Action1<String>() {
@Override
public void call(String s) {
Log.d("DDDDD","handle this---"+s)
}
}复制代码
之因此说它是非正式写法,是由于Action1是一个单纯的人畜无害的接口,和Observer没有啥关系,只不过它能够当作观察者来使,专门处理onNext 事件,这是一种为了简便偷懒的写法。固然还有Action0,Action2,Action3...,0,1,2,3分别表示call()这个方法能接受几个参数。若是你还不懂,能够暂时跳过。后面我也会尽可能使用new Subscriber方式,建立正统的观察者,便于大家理解。
如今已经建立了观察者和被观察者,可是二者尚未联系起来
switcher.subscribe(light);复制代码
我猜你看到这里应该有疑问了,为何是开关订阅了台灯?应该是台灯订阅了开关才对啊。卧槽,到底谁观察谁啊!!
你们冷静,把刀放下,有话慢慢说,
是这样的,台灯观察开关,逻辑是没错的,并且正常来看就应该是light.subscribe(switcher)才对,之因此“开关订阅台灯”,是为了保证流式API调用风格
啥是流式API调用风格?
//这就是RxJava的流式API调用
Observable.just("On","Off","On","On")
//在传递过程当中对事件进行过滤操做
.filter(new Func1<String, Boolean>() {
@Override
public Boolean call(String s) {
return s!=null;
}
})
.subscribe(mSubscriber);复制代码
上面就是一个很是简易的RxJava流式API的调用:同一个调用主体一路调用下来,一鼓作气。
因为被观察者产生事件,是事件的起点,那么开头就是用Observable这个主体调用来建立被观察者,产生事件,为了保证流式API调用规则,就直接让Observable做为惟一的调用主体,一路调用下去。
一句话,背后的真实的逻辑依然是台灯订阅了开关,可是在表面上,咱们让开关“伪装”订阅了台灯,以便于保持流式API调用风格不变。
好了,如今分解动做都完成了,已经架构了一个基本的RxJava事件处理流程。
咱们再来按照观察者模式的运做流程和流式Api的写法复习一遍:
流程图以下:
结合流程图的相应代码实例以下:
//建立被观察者,是事件传递的起点
Observable.just("On","Off","On","On")
//这就是在传递过程当中对事件进行过滤操做
.filter(new Func1<String, Boolean>() {
@Override
public Boolean call(String s) {
return s!=null;
}
})
//实现订阅
.subscribe(
//建立观察者,做为事件传递的终点处理事件
new Subscriber<String>() {
@Override
public void onCompleted() {
Log.d("DDDDDD","结束观察...\n");
}
@Override
public void onError(Throwable e) {
//出现错误会调用这个方法
}
@Override
public void onNext(String s) {
//处理事件
Log.d("DDDDD","handle this---"+s)
}
);复制代码
嗯,基本上咱们就把RxJava的骨架就讲完了,总结一下:
Tips: 当调用订阅操做(即调用Observable.subscribe()方法)的时候,被观察者才真正开始发出事件。
如今开始讲异步操做?别着急,事件的产生起点和处理的终点咱们都比较详细的讲解了,接下来咱们好好讲讲事件传递过程当中发生的那些事儿...
即便你已经看了我上面那段讲解,Rxjava可能还打动不了你,不要紧,事件产生的起点和消费的终点其实没那么吸引人,真正有意思的是事件传递过程当中的那些鬼斧神工的操做。
因为篇幅的限制,我只讲两三个操做,其余的操做请看个人RxJava操做的Demo(记得在github给我点star或者follow一下,否则我就坐在地上不起来,哼)
好比被观察者产生的事件中只有图片文件路径;,可是在观察者这里只想要bitmap,那么就须要类型变换。
Observable.just(getFilePath())
//使用map操做来完成类型转换
.map(new Func1<String, Bitmap>() {
@Override
public Bitmap call(String s) {
//显然自定义的createBitmapFromPath(s)方法,是一个极其耗时的操做
return createBitmapFromPath(s);
}
})
.subscribe(
//建立观察者,做为事件传递的终点处理事件
new Subscriber<Bitmap>() {
@Override
public void onCompleted() {
Log.d("DDDDDD","结束观察...\n");
}
@Override
public void onError(Throwable e) {
//出现错误会调用这个方法
}
@Override
public void onNext(Bitmap s) {
//处理事件
showBitmap(s)
}
);复制代码
你认真看完上面的代码就会以为,何须在过程当中变换类型呢?我直接在事件传递的终点,在观察者中变换就行咯。老实说,你这个想法没毛病,但实际上,上面写的代码是不合理的。
我在代码中也提到,读取文件,建立bitmap多是一个耗时操做,那么就应该在子线程中执行,主线程应该仅仅作展现。那么线程切换通常就会是比较复杂的事情了。可是在Rxjava中,是很是方便的。
Observable.just(getFilePath())
//指定了被观察者执行的线程环境
.subscribeOn(Schedulers.newThread())
//将接下来执行的线程环境指定为io线程
.observeOn(Schedulers.io())
//使用map操做来完成类型转换
.map(new Func1<String, Bitmap>() {
@Override
public Bitmap call(String s) {
//显然自定义的createBitmapFromPath(s)方法,是一个极其耗时的操做
return createBitmapFromPath(s);
}
})
//将后面执行的线程环境切换为主线程
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
//建立观察者,做为事件传递的终点处理事件
new Subscriber<Bitmap>() {
@Override
public void onCompleted() {
Log.d("DDDDDD","结束观察...\n");
}
@Override
public void onError(Throwable e) {
//出现错误会调用这个方法
}
@Override
public void onNext(Bitmap s) {
//处理事件
showBitmap(s)
}
);复制代码
由上面的代码能够看到,使用操做符将事件处理逐步分解,经过线程调度为每一步设置不一样的线程环境,彻底解决了你线程切换的烦恼。能够说线程调度+操做符,才真正展示了RxJava无与伦比的魅力。
先提出一个需求,查找一个学校每一个班级的每一个学生,并打印出来。
若是用老办法:先读出全部班级的数据,循环每一个班级。再循环中再读取每一个班级中每一个学生,而后循环打印出来。
仍是得说,这种想法,没毛病,就是嵌套得有点多。
Rxjava说:我不是针对谁...
//建立被观察者,获取全部班级
Observable.from(getSchoolClasses())
.flatMap(new Func1<SingleClass, Observable<Student>>() {
@Override
public Observable<Student> call(SingleClass singleClass) {
//将每一个班级的全部学生做为一列表包装成一列Observable<Student>,将学生一个一个传递出去
return Observable.from(singleClass.getStudents());
}
})
.subscribe(
//建立观察者,做为事件传递的终点处理事件
new Subscriber<Student>() {
@Override
public void onCompleted() {
Log.d("DDDDDD","结束观察...\n");
}
@Override
public void onError(Throwable e) {
//出现错误会调用这个方法
}
@Override
public void onNext(Student student) {
//接受到每一个学生类
Log.d("DDDDDD",student.getName())
}
);复制代码
好了,基本上按照RxJava的骨架搭起来就能完成需求。你说棒不棒??
其实FlatMap是比较难懂的一个操做符,做为初学者其实会用就好,因此我推荐的对于FlatMap的解释是:将每一个Observable产生的事件里的信息再包装成新的Observable传递出来,
那么为何FlatMap能够破除嵌套难题呢?
就是由于FlatMap能够再次包装新的Observable,而每一个Observable均可以使用from(T[])方法来建立本身,这个方法接受一个列表,而后将列表中的数据包装成一系列事件。
异步是相对于主线程来说的子线程操做,在这里咱们不妨使用线程调度这个概念更加贴切。
首先介绍一下RxJava的线程环境有哪些选项:
在讲解Map操做符时,已经提到了线程调度,在这里我用更加简介的代码代替:
//new Observable.just()执行在新线程
Observable.just(getFilePath())
//指定在新线程中建立被观察者
.subscribeOn(Schedulers.newThread())
//将接下来执行的线程环境指定为io线程
.observeOn(Schedulers.io())
//map就处在io线程
.map(mMapOperater)
//将后面执行的线程环境切换为主线程,
//可是这一句依然执行在io线程
.observeOn(AndroidSchedulers.mainThread())
//指定线程无效,但这句代码自己执行在主线程
.subscribeOn(Schedulers.io())
//执行在主线程
.subscribe(mSubscriber);复制代码
实际上线程调度只有subscribeOn()和observeOn()两个方法。对于初学者,只须要掌握两点:
subscribeOn()它指示Observable在一个指定的调度器上建立(只做用于被观察者建立阶段)。只能指定一次,若是指定屡次则以第一次为准
observeOn()指定在事件传递(加工变换)和最终被处理(观察者)的发生在哪个调度器。可指定屡次,每次指定完都在下一步生效。
线程调度掌握到这个程度,在入门阶段时绝对够用的了。
好了,对于RxJava整个入门文章到这里就彻底结束了,如今再来回看RxJava,你会发现,它就是在观察者模式的骨架下,经过丰富的操做符和便捷的异步操做来完成对于复杂业务的处理。
我相信你对于整个RxJava的骨架,以及执行流程应该有了至关的了解,如今就只须要多练习一下操做符的用法了。
本文没有介绍太多的操做符,不少没来得及介绍的操做符的用法实例都放在github上的RxJavaDemo项目上了,后期还会继续加上更多操做符的使用,欢迎你们上去看看,对照代码,手机运行一下。
你们多给点star!!顺便follow一下,接下来,我也会慢慢整理出一些别的有用的项目分享给你们。
暂无
若是你还有不太理解,或者以为文章在某些地方还有提高的空间,欢迎在下方留言,帮助我一块儿把这篇文章改得更加简单,生动,透彻,实用。这也是我写Blog一直追求的目标。
并且,我但愿这篇文章能成为RxJava入门上手最好的文章。
Rxjava2.0的文章也已经出来了啦,欢迎你们前去阅读。
欢迎转载,可是请注明出处。
star ~~