前言
使用RxJava 已经有一年多的时间,从 RxJava 1.0 +Retrofit 2.0 网络请求框架,包括常用的 RxBus,RxPermission 等,到后面的RxJava2.0+Retrofit2.0。然而始终没有认真细致的去归纳总结,基于这个缘由,本文将由浅入深,由Rx的基本用法,到RxJava和Retrofit的封装,到Rx的源码分析,做一次系统的学习。
https://github.com/ReactiveX/RxJava
本文将分为三个方面阐述:
- RxJava 是什么
- RxJava 基本用法
- RxJava 的用途
一:RxJava的认识
想要用一句话总结RxJava,或许只有作者的那句话才能精准概括了:RxJava实现了jvm的响应式扩展,通过观察序列来实现异步和基于事件的库。
如果要加上更细致的阐述那就是:它扩展了观察者模式,支持数据或者事件的序列,添加了一系列的操作符,允许你使用声明的方式组合序列,同时处理了线程、同步、线程安全、和并发的问题。
RxJava 依赖代码
compile "io.reactivex:rxjava:1.2.3"
compile "io.reactivex:rxandroid:1.2.1"
既然是观察者模式,那么就必然有观察者和被观察者,在RxJava中,有两个类需要搞清楚
Observable :被观察者,Observer :观察者。
二者通过 subscribe() 方法来实现订阅的关系,这样一来 Observable 可以在需要的时候发送事件通知 Observer。
Observable 的创建
//创建一个 Observable (被观察者)
Observable observable = Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber super String> subscriber) {
subscriber.onNext("hi");
subscriber.onCompleted();
}
});
创建 Observable 的时候,需要传递一个 OnSubscribe 对象 ,这个对象的指定了一个回调,当订阅事件发生的时候,call 方法里面的内容就会被调用。在回调的方法中 可以看到 Subscriber 对象,这个对象实现了 Observer 和 Subscription 接口,事实上,它是 Observer(观察者) 的升级版。
而 Observable 创建的时候做了什么呢?
public static Observable create(OnSubscribe f) {
return new Observable(RxJavaHooks.onCreate(f));
}
RxJavaHooks 的 onCreate() 返回了一个 OnSubscribe 对象,OnSubscribe 是 Action1 的子类,是一个只有一个 call() 方法的接口。RxJavaHooks 的内部,则调用了 RxJavaHooks 的一个接口 Func1 的 call()方法的回调。
Observer 的创建
//创建一个 Observer (观察者)
Observer observer = new Observer() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Object o) {
}
};
或者可以这样写:
Subscriber subscriber = new Subscriber() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Object o) {
}
};
Observer只是一个接口,他们的作用是一样的,区别在于 Subscriber 比 Observer 多了两个方法 onStart() 和 unsubscribe(), 前者是在事件还没有发送之前调用的,可以进行一些准备的动作,后者则是用于取消订阅,在发生订阅行为的时候,被观察者会持有观察者的引用,如果在页面销毁或者view销毁或者其他情况下没有进行解绑,则会发生内存泄漏。
订阅
//订阅
observable.subscribe(observer);
这里有个奇怪的现象,你会发现是被观察者订阅了观察者。之所以这样实现,是为了更好的设计。
而订阅行为到底干了什么,为什么要传递进去一个接口呢?看下源码
public final Subscription subscribe(final Observer super T> observer) {
if (observer instanceof Subscriber) {
return subscribe((Subscriber super T>)observer);
}
if (observer == null) {
throw new NullPointerException("observer is null");
}
return subscribe(new ObserverSubscriber(observer));
}
重点看下 observable 的 subscribe 方法,他需要传递一个 ObserverSubscriber的对象 ,它Subscriber 的子类,而且提供了一个构造函数来传递 observer。
在真正的 subscribe 方法中,我们可以看到这么一句话:
static Subscription subscribe(Subscriber super T> subscriber, Observable observable) {
// validate and proceed
if (subscriber == null) {
throw new IllegalArgumentException("subscriber can not be null");
}
if (observable.onSubscribe == null) {
throw new IllegalStateException("onSubscribe function can not be null.");
/*
* the subscribe function can also be overridden but generally that's not the appropriate approach
* so I won't mention that in the exception
*/
}
// new Subscriber so onStart it
subscriber.onStart();
/*
* See https://github.com/ReactiveX/RxJava/issues/216 for discussion on "Guideline 6.4: Protect calls
* to user code from within an Observer"
*/
// if not already wrapped
if (!(subscriber instanceof SafeSubscriber)) {
// assign to `observer` so we return the protected version
subscriber = new SafeSubscriber(subscriber);
}
// The code below is exactly the same an unsafeSubscribe but not used because it would
// add a significant depth to already huge call stacks.
try {
// allow the hook to intercept and/or decorate
RxJavaHooks.onObservableStart(observable, observable.onSubscribe).call(subscriber);
return RxJavaHooks.onObservableReturn(subscriber);
} catch (Throwable e) {
// special handling for certain Throwable/Error/Exception types
Exceptions.throwIfFatal(e);
// in case the subscriber can't listen to exceptions anymore
if (subscriber.isUnsubscribed()) {
RxJavaHooks.onError(RxJavaHooks.onObservableError(e));
} else {
// if an unhandled error occurs executing the onSubscribe we will propagate it
try {
subscriber.onError(RxJavaHooks.onObservableError(e));
} catch (Throwable e2) {
Exceptions.throwIfFatal(e2);
// if this happens it means the onError itself failed (perhaps an invalid function implementation)
// so we are unable to propagate the error correctly and will just throw
RuntimeException r = new OnErrorFailedException("Error occurred attempting to subscribe [" + e.getMessage() + "] and then again while trying to pass to onError.", e2);
// TODO could the hook be the cause of the error in the on error handling.
RxJavaHooks.onObservableError(r);
// TODO why aren't we throwing the hook's return value.
throw r; // NOPMD
}
}
return Subscriptions.unsubscribed();
}
}
subscriber.onStart(); 在 observable 中 调用了 subscriber 的 onStart()方法,
RxJavaHooks.onObservableStart(observable, observable.onSubscribe).call(subscriber); 这个方法,其实就是OnSubscribe对call 方法的调用。
经过上面的三个步骤,就完成了一次RxJava的基本使用。然而事实上,不管是 observable 的创建或者是 observer 或者 Subscriber ,再或者绑定的使用,都有很多种方法可以选择,事实上,要根据业务逻辑或者不同的场景来选择使用。比如:
Observable just = Observable.just( "are", "u", "ok");
just.subscribe(new Subscriber() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(String s) {
Log.i(TAG, s);
}
});
通过just 将传入的参数依次的发送出来,这样,在 Subscriber 的回调中(onNext),就可以依次的打印出那几个字符串。
假定有一个数据源,我需要依次发送数据源里面的数据,from方法则会很恰当,就像这样:
String[] array = new String[]{"are","u","ok"};
Observable observableStr = Observable.from(array);
observableStr.subscribe(new Subscriber() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(String s) {
}
});
同样的,Subscribe() 方法也可以通过Action来实现某些不完全的定义,比如这样:
Action1 action1 = new Action1() {
@Override
public void call(String s) {
Log.i(TAG, s);
}
};
observableStr.subscribe(action1);
Scheduler 调度器
通过 Schduler 调度器来指定某段代码运行在哪个线程之中,因为在日常开发中,我们总是会将耗时的请求放在后台,通过回调在前台更新ui,基于这些重要的线程控制,Scheduler 提供了一系列的方法来进行线程切换。诸如:
//直接运行,即在当前的线程运行
Schedulers.immediate();
//计算的线程,主要用于事件轮询,回调的处理等。不能进行耗时的操作。
Schedulers.computation();
//开启一个新的线程,并且运行在新线程
Schedulers.newThread();
// 读写、网络、等耗时操作
Schedulers.io();
//Android 主线程
AndroidSchedulers.mainThread();
Observable 提供了一些方法来指定我们需要的线程:
observableStr.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
subscribeOn() 方法指定了subscribe() 发生在IO线程,observeOn() 则指定了其回调发生在主线程。准确的说前者是指定事件产生的线程,后者是指定事件消费的线程。
变换
所谓变换,即把事件序列 中的对象或者整个序列进行变换,得到我想要的事件或者序列。通过一个简单的例子就可以看出来转换的强大之处了。
比如说我想加载指定路径下的ImageView
String mImgPath = "...";
Observable.just(mImgPath)
.map(new Func1() {
@Override
public Bitmap call(String s) {
Bitmap bitmap = BitmapFactory.decodeFile(s);
return bitmap;
}
})
.subscribe(new Action1() {
@Override
public void call(Bitmap bitmap) {
imageView.setImageBitmap(bitmap);
}
});
map()方法需要一个 Func1 的回调 ,Func1是一个简单的接口,和 Action 不同的地方在于,它是有返回值的。这就达到了转换的目的。我们看到,我们发送事件的只是一个字符串,通过转换,在我们需要Bitmap的时候,在map() 中把字符串做了转换。
除此之外,还有一个转换的方法 flatMap(),与map()方法不同的地方在于,flatMap中返回的是一个 Observable 对象。通过 flatMap 将事件进行了拆分,可以进行更加细致和精准的处理:
假如有这个一个类,在发起事件的时候传递整个类的对象,最后我想要的是String content,这种情况使用flatMap 就更加方便了。
public class DataBean implements Serializable {
public Data data;
public String message;
public int code;
class Data {
public String title;
public Content content;
}
class Content {
public String content;
}
}
final List beanList = new ArrayList<>();
Subscriber subscriber1 = new Subscriber() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(String s) {
}
};
Observable.from(beanList)
.flatMap(new Func1>() {
@Override
public Observable call(DataBean bean) {
return Observable.just(bean.data.content);
}
})
.flatMap(new Func1>() {
@Override
public Observable call(DataBean.Content content) {
return Observable.just(content.content);
}
})
.subscribe(subscriber1);
我们会发现 map 是一对一的关系,而 flatMap 是一对多的关系,通过 flatmap 可以实现很强大的功能。
小示例
假如我们需要把一个Bitmap格式的图片保存为png或者jpg文件 ,假如使用RxJava,结合上面的线程调度器,我们该怎么写呢?直接上代码吧。
Observable.create(new SaveObservable(bitmap))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SaveSubscriber());
//Observable
private class SaveObservable implements Observable.OnSubscribe {
private Bitmap drawingCache = null;
public SaveObservable(Bitmap drawingCache) {
this.drawingCache = drawingCache;
}
@Override
public void call(Subscriber super String> subscriber) {
if (drawingCache == null) {
subscriber.onError(new NullPointerException("获取图片失败"));
} else {
try {
File imageFile = new File(imagePath,imageName);
FileOutputStream outStream = new FileOutputStream(imageFile);
drawingCache.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
subscriber.onNext(Environment.getExternalStorageDirectory() + "/store");
subscriber.onCompleted();
outStream.flush();
outStream.close();
} catch (IOException e) {
subscriber.onError(e);
}
}
}
}
//Subscriber
private class SaveSubscriber extends Subscriber {
@Override
public void onCompleted() {
// Toast.makeText(context, "保存成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(Throwable e) {
Log.i(getClass().getSimpleName(), e.toString());
Toast.makeText(context, "保存失败:" + e.toString(), Toast.LENGTH_SHORT).show();
}
@Override
public void onNext(String s) {
Toast.makeText(context, "保存至" + s, Toast.LENGTH_SHORT).show();
}
}
这样以来,我们的代码看起来非常的清爽,而且逻辑清晰。