RxJava(十一)defer操作符实现代码支持链式调用
欢迎转载,转载请标明出处:
http://blog.csdn.net/johnny901114/article/details/52597643
本文出自:【余志强的博客】
一、前言
现在越来越多Android开发者使用到RxJava,在Android使用RxJava主要有如下好处:
1,轻松切换线程。以前我们切换线程主要使用Handler等手段来做。
2,轻松解决回调的嵌套问题。现在的app业务逻辑越来越复杂,多的时候3,4层回调嵌套,使得代码可维护性变得很差。RxJava链式调用使得这些调用变得扁平化。
随着RxJava的流行,越来越多的开源项目开始支持RxJava,像Retrofit、GreenDao等。这些开源项目支持RxJava使得我们解决复杂业务变得非常方便。
但是这些还不够,有的时候我们自己的封装的业务也需要支持RxJava,举个例子:查询数据、处理本地文件等操作,总而言之就是一些耗时任务。而且还要处理这些操作的成功、失败、线程切换等操作。
如果还是想以前那样做,那就太low。
二、下面就来探讨下如何使得代码支持RxJava风格
遇到这种问题,在我脑海里浮现的第一种方式就是通过Observable的create操作符。因为在里面我们可以控制数据的发射。就像上一篇文章那样《RxJava switchIfEmpty操作符实现Android检查本地缓存逻辑判断》
如下代码片段:
Observable.create(new Observable.OnSubscribe<Object>() {
@Override
public void call(Subscriber<? super Object> subscriber) {
try {
List<Article> as = articleDao.queryBuilder()
.where(ArticleDao.Properties.CategoryId.eq(categoryId))
.orderDesc(ArticleDao.Properties.Id)
.offset((pageIndex - 1) * pageSize)
.limit(pageSize).list();
if (as == null || as.isEmpty()) {
subscriber.onNext(null);
}else{
subscriber.onNext(as);
}
}catch (Exception e){
subscriber.onError(e);
}
subscriber.onCompleted();
}
});
这样确实没有没有问题。但是我们要封装下, 每个方法都这样写维护性和扩展比较差(例如有天我想换种方式来实现而不是create,如果通过方法封装一下,修改就变得容易多了)
如何封装呢?通过分析知道,大部分代码是相同的,只是我们的业务不一样。那么通过模板方法解决吧。业务方法通过接口回调的方式传递进来,因为我们不知道调用者是什么业务。
回调接口如下(T表示我们业务数据):
public interface MyCallable<T> {
T call();
}
下面是模板代码:
protected <R> Observable<R> createObservable(final MyCallable<R> callable) {
return Observable.create(new Observable.OnSubscribe<R>() {
@Override
public void call(Subscriber<? super R> subscriber) {
try {
R result = callable.call();
subscriber.onNext(result);
} catch (Exception e) {
subscriber.onError(e);
}
subscriber.onCompleted();
}
});
}
使用就非常简单了调用createObservable方法,实现MyCallable接口即可,然后就是跟使用RxJava一样处理逻辑。
三、分析greendao是如何支持RxJava风格的
看过Greendao源码的人知道,它也是通过这种方式支持RxJava的(下面看看他是怎么做的):
/**
* Rx version of {@link AbstractDao#loadAll()} returning an Observable.
*/
@Experimental
public Observable<T> load(final K key) {
return wrap(new Callable<T>() {
@Override
public T call() throws Exception {
return dao.load(key);
}
});
}
最终的实现也是通过dao.load(key)同步方法来实现的,关键是wrap方法了:
protected <R> Observable<R> wrap(Callable<R> callable) {
return wrap(RxUtils.fromCallable(callable));
}
//通过这个方法再包装了一层(就是默认设置执行的线程)
protected <R> Observable<R> wrap(Observable<R> observable) {
if (scheduler != null) {
return observable.subscribeOn(scheduler);
} else {
return observable;
}
}
通过代码可以看到默认执行的线程是IO线程:
/**
* The returned RxDao is a special DAO that let's you interact with Rx Observables using RX's IO scheduler for
* subscribeOn.
*
* @see #rxPlain()
*/
@Experimental
public RxDao<T, K> rx() {
if (rxDao == null) {
rxDao = new RxDao<>(this, Schedulers.io());
}
return rxDao;
}
所以使用greendao不用指定它在IO执行,因为框架已经帮我们设置了。
然后就是RxUtils.fromCallable(callable)方法了:
class RxUtils {
/** As of RxJava 1.1.7, Observable.fromCallable is still @Beta, so just in case... */
@Internal
static <T> Observable<T> fromCallable(final Callable<T> callable) {
return Observable.defer(new Func0<Observable<T>>() {
@Override
public Observable<T> call() {
T result;
try {
result = callable.call();
} catch (Exception e) {
return Observable.error(e);
}
return Observable.just(result);
}
});
}
}
上面的注释说通过Observable.fromCallable也可以实现这样的逻辑,也就是说代替Observable.defer()方法。
最后greendao是通过defer操作符来实现rx风格的。
四、defer和create操作符有什么异同点?
通过分析greendao源码得知,他是通过defer来做的,我们是通过create操作符来做的。那两者有什么不同?
我们对defer操作符比较陌生,先看看它的源码:
public static <T> Observable<T> defer(Func0<Observable<T>> observableFactory) {
return create(new OnSubscribeDefer<T>(observableFactory));
}
说白了就是调用了create(OnSubscribe<T> f) 方法:
public static <T> Observable<T> create(OnSubscribe<T> f) {
return new Observable<T>(hook.onCreate(f));
}
其实我们上面的create操作也是调用过来这个方法。只是defer操作符传递的OnSubscribe是OnSubscribeDefer,那我们来看看这是什么鬼?
public final class OnSubscribeDefer<T> implements OnSubscribe<T> {
final Func0<? extends Observable<? extends T>> observableFactory;
public OnSubscribeDefer(Func0<? extends Observable<? extends T>> observableFactory) {
this.observableFactory = observableFactory;
}
@Override
public void call(final Subscriber<? super T> s) {
Observable<? extends T> o;
try {
o = observableFactory.call();
} catch (Throwable t) {
Exceptions.throwOrReport(t, s);
return;
}
o.unsafeSubscribe(Subscribers.wrap(s));
}
}
OnSubscribeDefer也是继承自OnSubscribe,那么他的call方法肯定也是在订阅的时候被调用(就是说订阅的时候才创建这个observable,并且每次订阅都会创建一个新的observable)。
为什么Greendao没有使用create那种方式精确控制数据的发射?现在RxJava2.0对create操作符做出了一些限制,不能随随便便create了,这样出现一些问题。具体的RxJava2.0的改动可以看看
他的github说明What’s-different-in-2.0
五、参考资料:
pitfalls-of-operator-implementations
subscribe vs unsafeSubscribe
What’s-different-in-2.0
RxJava(十一)defer操作符实现代码支持链式调用的更多相关文章
- 自定义php-mysqli工具增强类,支持链式调用
<?php /*数据库访问类,支持链式访问 *function table($table):表名 *function where($where):条件 *function field(...$f ...
- js链式调用
我们都很熟悉jQuery了,只能jQuery中一种非常牛逼的写法叫链式操作 * $('#div').css('background','#ccc').removeClass('box').stop() ...
- C#中扩展StringBuilder支持链式方法
本篇体验扩展StringBuilder使之支持链式方法. 这里有一个根据键值集合生成select元素的方法. private static string BuilderSelectBox(IDicti ...
- jQuery支持链式编程,一句话实现左侧table页+常用筛选器总结
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- JavaScript运动_封装模板(支持链式运动、完美运动)
最近自学到了JS运动部分,自己整理了一些js模板,望采纳. 1.支持链式运动的模板: 先解释一下函数中的几个参数含义: 1)obj: 要操作的对象 2)target: 属性要到达的目标值 3)attr ...
- 史上最简单的手写Promise,仅17行代码即可实现Promise链式调用
Promise的使用相比大家已经孰能生巧了,我这里就不赘述了 先说说我写的Promise的问题吧,无法实现宏任务和微任务里的正确执行(也就是在Promise里面写setTimeout,setInter ...
- 如何写 JS 的链式调用 ---》JS 设计模式《----方法的链式调用
1.以$ 函数为例.通常返回一个HTML元素或一个元素集合. 代码如下: function $(){ var elements = []; ;i<arguments.length;i++){ v ...
- JavaScript设计模式——方法的链式调用
方法的链式调用: (function() { //私有类 function _$ (els) { this.elements = []; for(var i = 0, len = els.length ...
- 浅析 JavaScript 链式调用
对$函数你已经很熟悉了.它通常返回一个html元素或一个html元素的集合,如下: function$(){ var elements = []; for(vari=0,len=arguments.l ...
随机推荐
- Oracle表空间的管理方式
解释说明:表空间是一个逻辑概念:=> oracle 逻辑概念段区块管理方式: number one => tablespace number two=> segments Oracl ...
- 【SQL.基础构建-第一节(1/4)】
-- Tips:数据库与sql-- 一.What's 数据库-- 1.数据库(Database,DB):将大量数据保存起来,通过计算机加工而成的可以进行高效访问的数据集合.-- ...
- React Native(十五)——RN中的分享功能
终于,终于,可以总结自己使用RN时的分享功能了-- 为什么呢?且听我慢慢道来吧: 从刚开始接触React Native(2017年9月中旬)就着手于分享功能,直到自己参与公司的rn项目开发中,再到现在 ...
- [LeetCode] My Calendar II 我的日历之二
Implement a MyCalendarTwo class to store your events. A new event can be added if adding the event w ...
- [LeetCode] Print Binary Tree 打印二叉树
Print a binary tree in an m*n 2D string array following these rules: The row number m should be equa ...
- VINS 估计器之结构初始化
为什么要初始化 非线性VINS估计器的性能对于初始的速度,尺度,重力向量,空间点3D位置,以及外参等非常敏感.在很多场合中,能做到相机和IMU即插即用,线上自动校准与初始化,将会给用户带来极大的方便性 ...
- [BZOJ 2654]tree(陈立杰)
Description 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有need条白色边的生成树. 题目保证有解. Input 第一行V,E,need分别表示点数,边数和需要的白色 ...
- hdu 4352 数位dp + 状态压缩
XHXJ's LIS Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- BZOJ4727 [POI2017]Turysta
这题太神了还是去看刺儿神题解吧. http://www.cnblogs.com/neighthorn/p/6538364.html #include <cstdio> #include & ...
- Linux下的crontab定时、执行任务命令详解 oracle 自动备份
在LINUX中,周期执行的任务一般由cron这个守护进程来处理[ps -ef|grep cron].cron读取一个或多个配置文件,这些配置文件中包含了命令行及其调用时间.cron的配置文件称为&qu ...