rxjava 也用了好多次了。时隔多年,才终于有勇气挖一下它的源码。
这次主要研究了发布订阅流程 subscribe() 以及核心变换 lift(),至于其他的像线程切换 Schedulers、丰富的操作符啊,都有待研究。
之后可能会再更新几篇,也可能就太监了。。。
1.RxJava 源码分析之 —— lift 变换
RxJava 的思想是响应式编程,这是一种事件驱动的编程模式。事件驱动很熟悉吧,经典如通过 Listener 来监听点击事件再回调 OnClick(),这种方式早就烂大街了吧。RxJava 本质上与这种监听器没有区别,那么它又有什么独到之处呢?
它的独到之处在与它响应式的编程思想——一切皆抽象为事件。如果说对于点击事件,Android 本身已经提供了一套监听方案,那么网络请求呢,文件读写呢,还有数不清的事件呢。。此前,我们都是自己编写网络请求框架,然后对外提供回调接口 OnSuccess() 、OnFail()。换成文件读写,又得重新搞一套框架,我们累了,需要一个通用的解决方案,它就是 RxJava 。
By the way,解决了”回调地狱”只是顺带的好处罢了。
那么什么场景下使用它呢?
它的应用场合很多,本文将以网络请求为例做一个 Demo。
更多场景请参考这篇文章:
可能是东半球最全的RxJava使用场景小结
要使用 RxJava 还得深入到代码层面。
它是基于观察者模式实现的,那么自然会有一个被观察者(可观察的对象) Observable 和 一个观察者(订阅者)Subscriber。
// app/build.gradle
dependencies {
// 。。。
// rxjava
compile 'io.reactivex:rxjava:1.0.14'
compile 'io.reactivex:rxandroid:1.1.0'
// rxlifecycle
compile 'com.trello:rxlifecycle:0.4.0'
compile 'com.trello:rxlifecycle-components:0.4.0'
// 上面4个包是必要的
// okhttp
compile 'com.squareup.okhttp:okhttp:2.7.2'
// butterknife
compile 'com.jakewharton:butterknife:7.0.1'
}
最好配合上 retrolambda 这个插件,用 lambda 语法写起来才爽。
先来看一下 Subscriber 包含了哪些东西
// 打开源码我们看到 Subsctiber 实现了 Observer
public abstract class Subscriber<T> implements Observer<T>, Subscription { ... }
// 而 Observer 包含有一些回调函数
// {OnNext(), OnError(), OnComplete()}
public interface Observer<T> {
void onNext(T t);
void onError(Throwable e);
void onCompleted();
}
然后看看它怎么订阅事件
// Subscriber 作为监听器,需要注册到 Observable 中:
// (我们才能在 Observable 中回调它的一些函数)
mObservable.subscribe(mSubscriber);
// 这类似于我们注册点击事件的监听:
mView.setOnClickListener(mOnClickListener);
// 可以对应起来理解,View 即是被观察者,
// 而 OnClickListener 则是订阅者。
其中,mSubscriber 是这样的
// 当然,正如 mOnClickListener 实现了 OnClick(),
// mSubscriber 作为匿名类实现了三个接口函数,像这样:
Subscriber mSubscriber = new Subscriber() {
@Override
public void onNext(String s) { }
@Override
public void onCompleted() { }
@Override
public void onError(Throwable e) { }
};
一些简化写法:
// 考虑到这样的匿名类写法比较冗长,而且不能用 lambda 简化,
// 作者还提供了 subscribe(mSubscriber) 的一些重载版本:
mObservable.subscribe(onNext);
mObservable.subscribe(onNext, onError);
mObservable.subscribe(onNext, onError, onComplete);
// 太棒了,它们都是函数式接口,可以用 lambda 简化代码。
再来看一下 Observable , 之前说到它可以被 Subcriber 订阅。但是在此之前,我们需要先创建它。
// 创建 Observable 实例
mObservable.create(mOnSubscribe);
// 其中 mOnSubscribe 接口,用来制定事件分发的流程。
// 即按照一定顺序,回调之前注册好的三个函数:
// {OnNext(), OnError(), OnComplete()}
Observable.OnSubscribe mOnSubscribe = new Observable.OnSubscribe() {
@Override
public void call(Subscriber super String> subscriber) {
String data = getData(); // 获取数据: 网络任务或是别的...
if(data == null){ // 获取失败
subscriber.onError(new Throwable("data 为空"));
subscriber.onCompleted();
}else{ // 获取成功
subscriber.onNext(data);
subscriber.onCompleted();
}
}
};
What’s more,Observable 还可以分别指定 Observable 和 Subcriber 的线程:
Observable.create(subscriber -> {})
.subscribeOn(Schedulers.io()) // 指定 Observable 的线程 ———— 主线程。
.observeOn(AndroidSchedulers.mainThread()); // 指定 Subscriber 的线程 ———— io 线程。
框架内部估计封装了线程切换的功能,这在 Android 的多线程环境中,极大地简化了线程操作。
我们来试一下网络请求吧,从网上拉取一段字符串(http://publicobject.com/helloworld.txt)。首先,我们把一个请求看作一个事件,先创建一个事件对应的被观察者 NetworkObserable.getInstance() : Observable< String >。
public class NetworkObserable{
...
// 获取一个 Observable 实例
public static Observable getInstance() {
return Observable.create((Observable.OnSubscribe)
subscriber -> subscribeData(subscriber, NetworkHelper.getData(NetworkHelper.GET))
// getData()发起一个GET请求,返回 String 数据。
).subscribeOn(Schedulers.io());
// 被观察的对象在 io 线程进行网络请求,也可以自己新开一个线程
}
// 取得网络数据后,回调 subscriber 的一些函数,在界面上显示这些数据
private static void subscribeData(Subscriber super String> subscriber, String data){
if(data == null){
subscriber.onError(new Throwable("data 为空"));
subscriber.onCompleted();
}else{
subscriber.onNext(data);
subscriber.onCompleted();
}
}
}
在点击按钮的时候订阅这个事件。
@OnClick(R.id.btn_GET) public void onClick() {
NetworkObservable.getInstance() // 获取 Observable 实例
.compose(this.bindToLifecycle()) // Rxlifecycle, 绑定到 Activity 的生命周期。
.observeOn(AndroidSchedulers.mainThread()) // 指定观察者的线程————主线程。
.subscribe( // 订阅事件
// onNext, 请求成功
data -> mTvResult.setText(data),
// onError, 请求失败
throwable -> Toast.makeText(this, throwable.getMessage(), Toast.LENGTH_SHORT).show()
);
}
然后,没有然后了。。。
按照惯例,放上 Demo 地址,有兴趣可以下载一下:
https://github.com/fashare2015/my_rxjava_demo