RxJava从入门到不离不弃(一)——基本概念和使用
很久之前就想写篇文章,将RxJava的基本使用、各操作符和原理整理出来,分享给大家。断断续续地,看了许多大佬文章,结合自己的经验和想法,终于把它整理了出来,欢迎各位大佬拍砖。
更多内容,可以关注我的微信公众号——Android机动车
前言
RxJava的编程思想已经在Android开发者中变得越来越流行。有个不好的点就是上手不太容易,尤其是大部分人之前都是使用命令式编程语言。
首先要先理清这么一个问题:Rxjava和我们平时写的程序有什么不同。如果对Rxjava有过了解的朋友都会感受到用这种方式写的程序和我们一般写的程序有很明显的不同。我们一般写的程序叫做为命令式程序,是以流程为核心的,每一行代码实际上都是机器实际上要执行的指令。而Rxjava风格的代码,称为函数响应式编程。函数响应式编程是以数据流为核心,处理数据的输入,处理数据输出的。久而久之你会发现这个框架的精髓,尤其是运用到大项目中的时候,简直爱不释手,随着程序逻辑变得越来越复杂,它依然能够保持代码简洁。
认识RxJava
我们先来看看github上是怎么介绍RxJava的:
image翻译过来是什么意思呢? 博主直接请来谷歌翻译:一个用于使用Java VM的可观察序列编写异步和基于事件的程序的库。
归根结底,定义的核心在于异步。
RxJava的优点
还是一个字:简洁
异步操作很关键的一点是程序的简洁性,因为在调度过程比较复杂的情况下,异步代码经常会既难写也难被读懂。 Android 创造的 AsyncTask 和Handler ,其实都是为了让异步代码更加简洁。RxJava 的优势也是简洁,但它的简洁的与众不同之处在于,随着程序逻辑变得越来越复杂,它依然能够保持简洁。
随着对RxJava的深入了解,会更加深刻体会到RxJava的简洁带来的好处。
先举个栗子:
现在有这样一个需求:我们需要从网络下载一个zip,保存到指定文件夹,下载完成后进行解压,解压成功后在主线程进行UI操作。我们需要在子线程中进行下载和解压,完成后返回主线程操作。看下用RxJava如何实现:
retrofit.create(EmoticonDownloadService.class)
.download(url)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.map(new Func1<ResponseBody, InputStream>() {
@Override
public InputStream call(ResponseBody responseBody) {
return responseBody.byteStream();
}
})
.observeOn(Schedulers.computation()) // 用于计算任务
.doOnNext(new Action1<InputStream>() {
@Override
public void call(InputStream inputStream) {
writeFileAndUnZip(inputStream);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<InputStream>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
listener.onFail();
}
@Override
public void onNext(InputStream inputStream) {
// 进行UI更新
}
});
如果要用传统方式,需要开启子线程,在子线程中进行下载,然后进行解压,在返回主线程进行UI操作,嵌套层级和逻辑杂乱可想而知。当我们使用RxJava来做后,所有代码全部链式调用,逻辑清晰明了。这里要注意,我们所说的简洁,并不是指代码量少,而是结构清晰,便于阅读和修改。
基本概念
RxJava 是一个响应式编程框架,采用观察者设计模式。所以自然少不了 Observable 和 Subscriber了。
- Observable:发射源,英文释义“可观察的”,在观察者模式中称为“被观察者”或“可观察对象”;
- Observer:接收源,英文释义“观察者”,没错!就是观察者模式中的“观察者”,可接收Observable、Subject发射的数据;
- Subject:Subject是一个比较特殊的对象,既可充当发射源,也可充当接收源,为避免初学者被混淆,本章将不对Subject做过多的解释和使用,重点放在Observable和Observer上,先把最基本方法的使用学会,后面再学其他的都不是什么问题;
- Subscriber:订阅者,也是接收源,那它跟Observer有什么区别呢?Subscriber实现了Observer接口,比Observer多了一个最重要的方法unsubscribe( ),用来取消订阅,当你不再想接收数据了,可以调用unsubscribe( )方法停止接收,Observer 在 subscribe() 过程中,最终也会被转换成 Subscriber 对象,一般情况下,建议使用Subscriber作为接收源;
- Subscription:Observable调用subscribe( )方法返回的对象,同样有unsubscribe( )方法,可以用来取消订阅事件;
- Action0:RxJava中的一个接口,它只有一个无参call()方法,且无返回值,同样还有Action1,Action2...Action9等,Action1封装了含有* 1 个参的call()方法,即call(T t),Action2封装了含有 2 *个参数的call方法,即call(T1 t1,T2 t2),以此类推;
- Func0:与Action0非常相似,也有call()方法,但是它是有返回值的,同样也有Func0、Func1...Func9。
RxJava最核心的两个东西是Observable(被观察者,事件源)和Subscriber(观察者)。Observable发出一系列事件,Subscriber处理这些事件。
一个Observable可以发出0个或者多个事件,直到结束或者出错。每发出一个事件,就会调用它的Subscriber的onNext方法,最后调用Subscriber.onNext()或者Subscriber.onError()结束。
使用示例
ok,接下来看一个栗子:
Observable.just("Hello World!")
.map(new Func1<String, String>() {
@Override
public String call(String s) {
return s + "I am kyrie!";
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
Log.e("jia", "call: " + s);
}
});
事件发送源Observable发送一个字符串"Hello World!",使用map操作符(后面会介绍map操作符)将其转换为"Hello World! I am kyrie! ",最后交给观察者Subscriber处理,将其打印。
当然,RxJava的操作符结合lambda表达式,代码会更加简洁干练:
Observable.just("Hello World!")
.map(s -> s + "I am kyrie!")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s -> {
Log.e("jia", "call: " + s);
});
让项目支持lambda表达式,需要再build.gradle中配置如下:
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
基本使用
Observable的创建
Observable<String> observable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("Hi,Weavey!");
subscriber.onNext("Hi,!");
subscriber.onCompleted();
subscriber.onNext("Hi,js!");
}
});
可以看到,这里传入了一个 OnSubscribe 对象作为参数。OnSubscribe 会被存储在返回的 Observable 对象中,它的作用相当于一个计划表,当 Observable 被订阅的时候,OnSubscribe 的 call() 方法会自动被调用,事件序列就会依照设定依次触发。这样,由被观察者调用了观察者的回调方法,就实现了由被观察者向观察者的事件传递,即观察者模式。
这个例子只是简单解释下Observable的基础创建,在实际生产中并无意义。
上面的例子中,计划表依次发出两个字符串,然后通知完成,之后的第三个字符串便不会再发送。也就是说,只要执行一次subscriber的onCompleted或onError方法,之后的事件就不会再发送。
Observer的创建
Observer observer = new Observer<String>() {
@Override
public void onCompleted() {
Log.e("jia", "onCompleted: ");
}
@Override
public void onError(Throwable e) {
Log.e("jia", "onError: " + e.toString());
}
@Override
public void onNext(String o) {
Log.e("jia", "onNext: " + o.toString());
}
};
Observer 是一个接口,包含三个抽象方法:onNext、onError、onCompleted。
每次正常接收到消息,都会执行onNext方法,如果过程中出现异常,或显式调用subscriber的onError,则会执行onError方法,如果正常全部执行完毕,会调用onCompleted方法。
除了 Observer 接口之外,RxJava 还内置了一个实现了 Observer 的抽象类:Subscriber。Subscriber 对 Observer 接口进行了一些扩展,但他们的基本使用方式是完全一样的,实质上,在 RxJava 的 subscribe 过程中,Observer 也总是会先被转换成一个 Subscriber 再使用。所以如果你只想使用基本功能,选择 Observer 和 Subscriber 是完全一样的。它们的区别对于使用者来说主要有两点:
- onStart(): 这是 Subscriber 增加的方法。它会在 subscribe 刚开始,而事件还未发送之前被调用,可以用于做一些准备工作,例如数据的清零或重置。这是一个可选方法,默认情况下它的实现为空。
需要注意的是,如果对准备工作的线程有要求(例如弹出一个显示进度的对话框,这必须在主线程执行),onStart() 就不适用了,因为它总是在 subscribe 所发生的线程被调用,而不能指定线程。要在指定的线程来做准备工作,可以使用 doOnSubscribe() 方法。
- unsubscribe(): 这是 Subscriber 所实现的另一个接口 Subscription 的方法,用于取消订阅。在这个方法被调用后,Subscriber 将不再接收事件。一般在这个方法调用前,可以使用 isUnsubscribed() 先判断一下状态。
所以Observer我们一般这样创建:
Observer observer = new Subscriber<String>() {
/**
* Subscriber特有方法,事件还未发送前调用,但注意是在subscribe执行的线程中
*/
@Override
public void onStart() {
super.onStart();
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(String s) {
}
};
订阅
我们使用subscribe完成事件的订阅。
observable.subscribe(subscriber);
Observable和Observer的关联订阅之后会返回一个Subscription对象。
Subscription subscription = Observable.just("Hello, World!")
.subscribe(s -> System.out.println(s));
if (!subscription.isUnsubscribed())
subscription.unsubscribe();
unsubscribe() 这个方法很重要,因为在 subscribe() 之后, Observable 会持有 Subscriber 的引用,这个引用如果不能及时被释放,将有内存泄露的风险。所以最好保持一个原则:要在不再使用的时候尽快在合适的地方(例如 onPause() onStop() 等方法中)调用 unsubscribe() 来解除引用关系,以避免内存泄露的发生。
调用unsubscribing后,会停止整个调用链。如果你使用了一串很复杂的操作符,调用unsubscribe将会在他当前执行的地方终止。不需要做任何额外的工作。
更多精彩内容,欢迎关注我的微信公众号——Android机动车
image