本文是《Android开发之深度项目设计探索》系列的第四篇,主要介绍的是 基于最新Rxjava2类库的使用及部分操作符、实际项目中Rxjava2代码抽取封装进行说明分析,本系列历史文章:
《Android开发之深度项目设计探索(一)》
《Android开发之深度项目设计探索(二)》
《Android开发之深度项目设计探索(三)》
Rxjava2的设计理念
关于Rxjava2,已经有很多大佬分享了他们的使用和心得,我这里就简单描述下(注:要想用一篇文章写完Rxjava的前世今生和各种操作符的确很难,所以本篇文章主要是介绍基础概念以及一些对封装了Rxjava代码进行分析)。有很多朋友跟我说到,简单的Rxjava使用还是会,可是看到别人(公司内部)封装的Rxjava代码就晕晕乎乎的看不懂。由于Rxjava2是Rxjava1的拓展和延伸、内部主要是用到了 观察者模式(也有大佬为了更加具象化这种设计模式,称这种思想为管道流,管道流简单点理解就是上流指向下流的一种数据流向)。因此,在分析代码前还是要理一下观察者模式。
观察者模式简单理解就是:当被观察者发生变化,及时通知观察者(类似广播和EventBus)。所以,Rxjava2中的被观察者和观察者在代码中表现的就分别是Observable,Observer ,注意:被观察者和观察者之间的关系是一对一或者一对多的关系,按照Java万物皆对象的设计理念,被观察者和观察者需要构建关系才来联合使用,这里的联合,在Rxjava中,就是 订阅 。毕竟,只有将被观察者和观察者两者联合起来才有意义(这里的意义,指的是数据的传递,试想如果被观察者和观察者两者之间无法构建订阅,想要传递的数据也就失去了方向),下面这个表格主要是为了加深记忆和方便日后使用
被观察者 | 观察者 | 订阅 |
---|---|---|
Observable | Observer | subscribe |
Rxjava2 的基本写法
毫无疑问,下面代码是大家最熟悉的Rxjava2写法:
//大家最熟悉的Rxjava2写法
private void initOriginRxjava() {
//创建被观察者
Observable stringObservable = Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter e) throws Exception {
//发射数据
e.onNext("我的");
e.onNext("天啦");
}
});
//创建观察者
Observer myObserver = new Observer() {
@Override
public void onSubscribe(Disposable d) {
Log.i(TAG, "onSubscribe: ");
}
@Override
public void onNext(String value) {
Log.i(TAG, "onNext: "+value);
}
@Override
public void onError(Throwable e) {
Log.i(TAG, "onError: ");
}
@Override
public void onComplete() {
Log.i(TAG, "onComplete: ");
}
};
}
相信这个写法大家非常熟悉(因为很多博客就是这样写的),上述代码定义了被观察者和观察者也就是Observable,Observer。两个角色定义完毕,那么这两者的订阅在代码中该如何表示?订阅,在Rxjava2中也就是 subscribe() ,如下图:
可以看到,subscribe是一个方法重载,该方法可以不用传参数,也可以传入Consumer(红色截图),也可以传入观察者 Observer( 黄色截图),由于上面的代码已经创建了观察者 Observer,且为了理清楚,直接传入一个Observer(直接传Observer也是最熟悉的方式):
订阅 - 1:传入Observer
//订阅1:传递一个观察者
stringObservable.subscribe(myObserver);
这种订阅,是我们最熟悉的写法,也是最基础的写法,Observable中的数据源最后传递到了Observer中的onNext(T value)中,
运行代码之后,日志显示如下:
com.example.ly.mystudy I/rxjava2: onSubscribe:
com.example.ly.mystudy I/rxjava2: onNext: 我的
com.example.ly.mystudy I/rxjava2: onNext: 天啦
日志显示:首先调用了onSubscribe(),这里传递参数为Disposable,这个Disposable 相当于 RxJava 1.x 中的 Subscription, 主要功能是用于解除订阅,解除订阅的意思就是被观察者(Observable)还可以发射数据,但是观察者(Observer)无法收到收据。
订阅 - 1:传入Observer之链式写法
大家可能更加习惯是将Observable和Observer连起来(也就是链式写法),基础写法如下:
//Rxjava2链式基础写法
private void initOriginRxjava2() {
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter e) throws Exception {
e.onNext("你好啊");
e.onNext("骑小猪看流星");
e.onNext("改革春风吹满地");
e.onNext("中国人民真争气");
e.onNext("这个世界太疯狂");
}
}).subscribe(new Observer() {
Disposable mDisposable;
@Override
public void onSubscribe(Disposable d) {
//解除订阅
mDisposable = d;
Log.i(TAG, "onSubscribe: ");
}
@Override
public void onNext(String value) {
if ("改革春风吹满地".equals(value)){
//满足条件则解除订阅
//解除订阅后 观察者则无法收到被观察者传来的数据源
mDisposable.dispose();
}
Log.i(TAG, "onNext: "+value);
}
@Override
public void onError(Throwable e) {
Log.i(TAG, "onError: ");
}
@Override
public void onComplete() {
Log.i(TAG, "onComplete: ");
}
});
}
这种写法和上面的写法本质没什么大的区别,只是把 new Observer的过程放进了subscibe中,然后这里用到了 Disposable,上面也说到了 Disposable 主要功能是用于解除订阅,解除订阅后观察者(Observer)就无法收到收据。因此这个 Disposable 就可以根据实际开发条件来进行操作和限制了。
运行代码之后,日志显示如下:
com.example.ly.mystudy I/rxjava2: onSubscribe:
com.example.ly.mystudy I/rxjava2: onNext: 你好啊
com.example.ly.mystudy I/rxjava2: onNext: 骑小猪看流星
com.example.ly.mystudy I/rxjava2: onNext: 改革春风吹满地
订阅 - 2:传入Consumer
上文说到subscribe()是一个方法重载,该方法也可以传入Consumer,那么这个Consumer是神马?点进源码看看
/**
* A functional interface (callback) that accepts a single value.
* @param the value type
*/
public interface Consumer {
/**
* Consume the given value.
* @param t the value
* @throws Exception on error
*/
void accept(T t) throws Exception;
}
类注释翻译过来就是:这个接口的功能主要是用来接受唯一值。因此,这个值的类型就是声明的泛型,可以发现,accept方法内部抛了异常,throws Exception放在方法后边,意味着本方法不处理异常,交给被调用者处理(如果不希望异常层层往上抛,你就要用throws Exception) ,而且被调用者必须处理。
因此,Consumer简单理解它就是一个简易版的观察者(Observer)它主要是用来 接受单个值的,也就是通过accept(T t)来 接受 被观察者传过来的值
下面是 Consumer 基本的写法,也是最常见的写法:
private void initOrginConsumer() {
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter e) throws Exception {
e.onNext(" consumer : "+"改革春风吹满地");
e.onNext(" consumer : "+"中国人民真争气");
e.onNext(" consumer : "+"这个世界太疯狂");
}
}).subscribe(new Consumer() {
@Override
public void accept(String s) throws Exception {
Log.i(TAG, " accept: "+ s);
}
});
}
运行代码之后,日志显示如下:
com.example.ly.mystudy I/rxjava2: accept: consumer : 改革春风吹满地
com.example.ly.mystudy I/rxjava2: accept: consumer : 中国人民真争气
com.example.ly.mystudy I/rxjava2: accept: consumer : 这个世界太疯狂
在实际or商业项目中,一般会把Consumer单独抽取出来,因为它是一个接口,实现该接口并在需要的位置传入具体的类也是可以的,
- 实现Consumer接口:
public class TestConsumer implements Consumer {
@Override
public void accept(String stirng) throws Exception {
Log.i("info", "accept: "+stirng);
}
}
- subscribe( )传入该实现类:
//传入 Consumer接口实现对象
private void initSubConsumer() {
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter e) throws Exception {
e.onNext(" consumer : "+"改革春风吹满地");
e.onNext(" consumer : "+"中国人民真争气");
e.onNext(" consumer : "+"这个世界太疯狂");
}
}).subscribe(new TestConsumer());
}
订阅 - 1 和 订阅 - 2的总结与区别:
订阅 - 1,使用了Observer,重载了4个方法,这4个方法的作用非常明显,可以解除订阅(拦截)、可以抛异常,可以接收数据,因此它是很基础和全面的;而订阅 - 2,个人的理解是,Consumer设计之初更加侧重的是 数据的传递(single value),简化代码提高效率,名副其实的 不管黑猫白猫,有数据就是好猫。
Rxjava2 的线程切换
所谓的线程切换,Rxjava2是基于Android诞生之初的理念来设计的,因为数据(请求、响应)的处理是耗时的,而Android又要在UI线程绘制界面来进行功能展示。如果在主线程执行耗时的操作,就可能会造成后面任务的阻塞、UI的卡顿等等一系列麻烦事情。为了解决这个问题,从早期的Handle,AsyncTask到现在的Rxjava2,都看见了线程切换的必然性。
言归正传,还是回到观察者模式,这种模式的本质是 被观察者 发生变化 才会去 通知观察者 ,因此,数据的产生实际上是源于被观察者(Observable), 产生的数据通过订阅指向给观察者,所以,观察者(Observer)最终会拿到数据。
综上,由于数据的产生(Observable)一般会通过后台接口来获取,这种耗时的操作基本上放在子线程来进行;由于观察者(Observer)最终会拿到数据 ,而Android拿到数据的目的大都是为了更新UI,因此,更新UI的Observer一般是在主线程。
Rxjava2中的线程切换,主要是
subscribeOn(Scheduler scheduler)
observeOn(Scheduler scheduler)
一些朋友说,看见这2个就分不清楚,简单的记忆办法是看见subscribe就想到订阅,看见observe就想起观察者,这样记忆就不会错了。
首先看下subscribeOn()的源码:
/**
* Asynchronously subscribes Observers to this ObservableSource on the specified {@link Scheduler}.
* Scheduler:
* You specify which {@link Scheduler} this operator will use
* @param scheduler the {@link Scheduler} to perform subscription actions on
* @return the source ObservableSource modified so that its subscriptions happen on the specified {@link Scheduler}
*/
@SchedulerSupport(SchedulerSupport.CUSTOM)
public final Observable subscribeOn(Scheduler scheduler) {
ObjectHelper.requireNonNull(scheduler, "scheduler is null");
return RxJavaPlugins.onAssembly(new ObservableSubscribeOn(this, scheduler));
}
通过英文注释,这个方法的主要目的是:修改源ObservableSource,使其订阅发生在指定的调度程序。由于参数Scheduler的源码过多,这里就不进行说明,它主要是提供线程模型来进行线程切换,Scheduler常见的线程模型有以下四种:
Schedulers.io() :代表io操作的线程, 通常用于网络,读写文件等io密集型的操作;
Schedulers.computation() :代表CPU计算密集型的操作, 例如需要大量计算的操作;
Schedulers.newThread() :代表一个常规的新线程;
AndroidSchedulers.mainThread() :代表Android的主线程、UI线程
observeOn()是指定观察者接收数据的线程,一般指定为主线程即可,因此,在发射数据的时候;指定为子线程,接收数据为主线程,这样就可以进行合理的线程切换了。
//Rxjava2 线程调度
private void initRxjavaSchedulers() {
//创建被观察者
Observable stringObservable = Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter e) throws Exception {
Log.i(TAG, "Observable thread == " + Thread.currentThread().getName());
e.onNext("改革春风吹满地");
e.onNext("中国人民真争气");
e.onNext("这个世界太疯狂");
}
});
//创建观察者
Observer myObserver = new Observer() {
@Override
public void onSubscribe(Disposable d) {
Log.i(TAG, "onSubscribe: ");
}
@Override
public void onNext(String value) {
Log.i(TAG, "Observer thread == " + Thread.currentThread().getName());
Log.i(TAG, "onNext: "+value);
}
@Override
public void onError(Throwable e) {
Log.i(TAG, "onError: ");
}
@Override
public void onComplete() {
Log.i(TAG, "onComplete: ");
}
};
//订阅、线程切换
stringObservable.subscribeOn(Schedulers.newThread()).
observeOn(AndroidSchedulers. mainThread()).
subscribe(myObserver);
}
运行日志如下:
com.example.ly.mystudy I/rxjava2: onSubscribe:
com.example.ly.mystudy I/rxjava2: Observable thread == RxNewThreadScheduler-1
com.example.ly.mystudy I/rxjava2: Observer thread == main
com.example.ly.mystudy I/rxjava2: onNext: 改革春风吹满地
com.example.ly.mystudy I/rxjava2: Observer thread == main
com.example.ly.mystudy I/rxjava2: onNext: 中国人民真争气
com.example.ly.mystudy I/rxjava2: Observer thread == main
com.example.ly.mystudy I/rxjava2: onNext: 这个世界太疯狂
本篇文章没有涉及到Rxjava2的操作符,仅介绍了Rxjava2的基础。基础的重要性不言而喻,打好基础就好比曾阿牛学会了九阳神功,他学其他的武功那就是易如反掌
(未完待续......)
如果这篇文章对您有开发or学习上的些许帮助,希望各位看官留下宝贵的star,谢谢。
Ps:著作权归作者所有,转载请注明作者, 商业转载请联系作者获得授权,非商业转载请注明出处(开头或结尾请添加转载出处,添加原文url地址),文章请勿滥用,也希望大家尊重笔者的劳动成果