这可能是最好的RxJava 2.x入门教程系列专栏

文章链接:

这可能是最好的RxJava 2.x 入门教程(完结版)【强力推荐】

这可能是最好的RxJava 2.x 入门教程(一)

这可能是最好的RxJava 2.x 入门教程(二)

这可能是最好的RxJava 2.x 入门教程(三)

这可能是最好的RxJava 2.x 入门教程(四)

这可能是最好的RxJava 2.x 入门教程(五)

GitHub 代码同步更新:https://github.com/nanchen2251/RxJava2Examples

为了满足大家的饥渴难耐,GitHub将同步更新代码,主要包含基本的代码封装,RxJava 2.x所有操作符应用场景介绍和实际应用场景,后期除了RxJava可能还会增添其他东西,总之,GitHub上的Demo专为大家倾心打造。传送门:https://github.com/nanchen2251/RxJava2Examples

一、前言

终于如愿来到让我小伙伴们亢奋的 RxJava 2 使用场景举例了,前面几章中我们讲解完了 RxJava 1.x 到 RxJava 2.x 的异同以及 RxJava 2.x 的各种操作符使用,如有疑问,欢迎点击上方的链接进入你想要的环节。

二、正题

1、简单的网络请求

想必大家都知道,很多时候我们在使用 RxJava 的时候总是和 Retrofit 进行结合使用,而为了方便演示,这里我们就暂且采用 OkHttp3 进行演示,配合 map,doOnNext ,线程切换进行简单的网络请求:

1)通过 Observable.create() 方法,调用 OkHttp 网络请求;

2)通过 map 操作符集合 gson,将 Response 转换为 bean 类;

3)通过 doOnNext() 方法,解析 bean 中的数据,并进行数据库存储等操作;

4) 调度线程,在子线程中进行耗时操作任务,在主线程中更新 UI ;

5) 通过 subscribe(),根据请求成功或者失败来更新 UI 。

Observable.create(new ObservableOnSubscribe<Response>() {
@Override
public void subscribe(@NonNull ObservableEmitter<Response> e) throws Exception {
Builder builder = new Builder()
.url("http://api.avatardata.cn/MobilePlace/LookUp?key=ec47b85086be4dc8b5d941f5abd37a4e&mobileNumber=13021671512")
.get();
Request request = builder.build();
Call call = new OkHttpClient().newCall(request);
Response response = call.execute();
e.onNext(response);
}
}).map(new Function<Response, MobileAddress>() {
@Override
public MobileAddress apply(@NonNull Response response) throws Exception { Log.e(TAG, "map 线程:" + Thread.currentThread().getName() + "\n");
if (response.isSuccessful()) {
ResponseBody body = response.body();
if (body != null) {
Log.e(TAG, "map:转换前:" + response.body());
return new Gson().fromJson(body.string(), MobileAddress.class);
}
}
return null;
}
}).observeOn(AndroidSchedulers.mainThread())
.doOnNext(new Consumer<MobileAddress>() {
@Override
public void accept(@NonNull MobileAddress s) throws Exception {
Log.e(TAG, "doOnNext 线程:" + Thread.currentThread().getName() + "\n");
mRxOperatorsText.append("\ndoOnNext 线程:" + Thread.currentThread().getName() + "\n");
Log.e(TAG, "doOnNext: 保存成功:" + s.toString() + "\n");
mRxOperatorsText.append("doOnNext: 保存成功:" + s.toString() + "\n"); }
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<MobileAddress>() {
@Override
public void accept(@NonNull MobileAddress data) throws Exception {
Log.e(TAG, "subscribe 线程:" + Thread.currentThread().getName() + "\n");
mRxOperatorsText.append("\nsubscribe 线程:" + Thread.currentThread().getName() + "\n");
Log.e(TAG, "成功:" + data.toString() + "\n");
mRxOperatorsText.append("成功:" + data.toString() + "\n");
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
Log.e(TAG, "subscribe 线程:" + Thread.currentThread().getName() + "\n");
mRxOperatorsText.append("\nsubscribe 线程:" + Thread.currentThread().getName() + "\n"); Log.e(TAG, "失败:" + throwable.getMessage() + "\n");
mRxOperatorsText.append("失败:" + throwable.getMessage() + "\n");
}
});

为了方便,我们后面的讲解大部分采用开源的 Rx2AndroidNetworking 来处理,数据来源于天狗网等多个公共API接口。

mRxOperatorsText.append("RxNetworkActivity\n");
Rx2AndroidNetworking.get("http://api.avatardata.cn/MobilePlace/LookUp?key=ec47b85086be4dc8b5d941f5abd37a4e&mobileNumber=13021671512")
.build()
.getObjectObservable(MobileAddress.class)
.observeOn(AndroidSchedulers.mainThread()) // 为doOnNext() 指定在主线程,否则报错
.doOnNext(new Consumer<MobileAddress>() {
@Override
public void accept(@NonNull MobileAddress data) throws Exception {
Log.e(TAG, "doOnNext:"+Thread.currentThread().getName()+"\n" );
mRxOperatorsText.append("\ndoOnNext:"+Thread.currentThread().getName()+"\n" );
Log.e(TAG,"doOnNext:"+data.toString()+"\n");
mRxOperatorsText.append("doOnNext:"+data.toString()+"\n");
}
})
.map(new Function<MobileAddress, ResultBean>() {
@Override
public ResultBean apply(@NonNull MobileAddress mobileAddress) throws Exception {
Log.e(TAG, "\n" );
mRxOperatorsText.append("\n");
Log.e(TAG, "map:"+Thread.currentThread().getName()+"\n" );
mRxOperatorsText.append("map:"+Thread.currentThread().getName()+"\n" );
return mobileAddress.getResult();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<ResultBean>() {
@Override
public void accept(@NonNull ResultBean data) throws Exception {
Log.e(TAG, "subscribe 成功:"+Thread.currentThread().getName()+"\n" );
mRxOperatorsText.append("\nsubscribe 成功:"+Thread.currentThread().getName()+"\n" );
Log.e(TAG, "成功:" + data.toString() + "\n");
mRxOperatorsText.append("成功:" + data.toString() + "\n");
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
Log.e(TAG, "subscribe 失败:"+Thread.currentThread().getName()+"\n" );
mRxOperatorsText.append("\nsubscribe 失败:"+Thread.currentThread().getName()+"\n" );
Log.e(TAG, "失败:"+ throwable.getMessage()+"\n" );
mRxOperatorsText.append("失败:"+ throwable.getMessage()+"\n");
}
});

2、先读取缓存,如果缓存没数据再通过网络请求获取数据后更新UI

想必在实际应用中,很多时候(对数据操作不敏感时)都需要我们先读取缓存的数据,如果缓存没有数据,再通过网络请求获取,随后在主线程更新我们的UI。

concat 操作符简直就是为我们这种需求量身定做。

concat 可以做到不交错的发射两个甚至多个 Observable 的发射事件,并且只有前一个 Observable 终止(onComplete) 后才会定义下一个 Observable。

利用这个特性,我们就可以先读取缓存数据,倘若获取到的缓存数据不是我们想要的,再调用 onComplete() 以执行获取网络数据的Observable,如果缓存数据能应我们所需,则直接调用 onNext() ,防止过度的网络请求,浪费用户的流量。

Observable<FoodList> cache = Observable.create(new ObservableOnSubscribe<FoodList>() {
@Override
public void subscribe(@NonNull ObservableEmitter<FoodList> e) throws Exception {
Log.e(TAG, "create当前线程:"+Thread.currentThread().getName() );
FoodList data = CacheManager.getInstance().getFoodListData(); // 在操作符 concat 中,只有调用 onComplete 之后才会执行下一个 Observable
if (data != null){ // 如果缓存数据不为空,则直接读取缓存数据,而不读取网络数据
isFromNet = false;
Log.e(TAG, "\nsubscribe: 读取缓存数据:" );
runOnUiThread(new Runnable() {
@Override
public void run() {
mRxOperatorsText.append("\nsubscribe: 读取缓存数据:\n");
}
}); e.onNext(data);
}else {
isFromNet = true;
runOnUiThread(new Runnable() {
@Override
public void run() {
mRxOperatorsText.append("\nsubscribe: 读取网络数据:\n");
}
});
Log.e(TAG, "\nsubscribe: 读取网络数据:" );
e.onComplete();
} }
}); Observable<FoodList> network = Rx2AndroidNetworking.get("http://www.tngou.net/api/food/list")
.addQueryParameter("rows",10+"")
.build()
.getObjectObservable(FoodList.class); // 两个 Observable 的泛型应当保持一致 Observable.concat(cache,network)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<FoodList>() {
@Override
public void accept(@NonNull FoodList tngouBeen) throws Exception {
Log.e(TAG, "subscribe 成功:"+Thread.currentThread().getName() );
if (isFromNet){
mRxOperatorsText.append("accept : 网络获取数据设置缓存: \n");
Log.e(TAG, "accept : 网络获取数据设置缓存: \n"+tngouBeen.toString() );
CacheManager.getInstance().setFoodListData(tngouBeen);
} mRxOperatorsText.append("accept: 读取数据成功:" + tngouBeen.toString()+"\n");
Log.e(TAG, "accept: 读取数据成功:" + tngouBeen.toString());
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
Log.e(TAG, "subscribe 失败:"+Thread.currentThread().getName() );
Log.e(TAG, "accept: 读取数据失败:"+throwable.getMessage() );
mRxOperatorsText.append("accept: 读取数据失败:"+throwable.getMessage()+"\n");
}
});

有时候我们的缓存可能还会分为 memory 和 disk ,实际上都差不多,无非是多写点 Observable ,然后通过 concat 合并即可。

3、多个网络请求依次依赖

想必这种情况也在实际情况中比比皆是,例如用户注册成功后需要自动登录,我们只需要先通过注册接口注册用户信息,注册成功后马上调用登录接口进行自动登录即可。

我们的 flatMap 恰好解决了这种应用场景,flatMap 操作符可以将一个发射数据的 Observable 变换为多个 Observables ,然后将它们发射的数据合并后放到一个单独的 Observable,利用这个特性,我们很轻松地达到了我们的需求。

Rx2AndroidNetworking.get("http://www.tngou.net/api/food/list")
.addQueryParameter("rows", 1 + "")
.build()
.getObjectObservable(FoodList.class) // 发起获取食品列表的请求,并解析到FootList
.subscribeOn(Schedulers.io()) // 在io线程进行网络请求
.observeOn(AndroidSchedulers.mainThread()) // 在主线程处理获取食品列表的请求结果
.doOnNext(new Consumer<FoodList>() {
@Override
public void accept(@NonNull FoodList foodList) throws Exception {
// 先根据获取食品列表的响应结果做一些操作
Log.e(TAG, "accept: doOnNext :" + foodList.toString());
mRxOperatorsText.append("accept: doOnNext :" + foodList.toString()+"\n");
}
})
.observeOn(Schedulers.io()) // 回到 io 线程去处理获取食品详情的请求
.flatMap(new Function<FoodList, ObservableSource<FoodDetail>>() {
@Override
public ObservableSource<FoodDetail> apply(@NonNull FoodList foodList) throws Exception {
if (foodList != null && foodList.getTngou() != null && foodList.getTngou().size() > 0) {
return Rx2AndroidNetworking.post("http://www.tngou.net/api/food/show")
.addBodyParameter("id", foodList.getTngou().get(0).getId() + "")
.build()
.getObjectObservable(FoodDetail.class);
}
return null; }
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<FoodDetail>() {
@Override
public void accept(@NonNull FoodDetail foodDetail) throws Exception {
Log.e(TAG, "accept: success :" + foodDetail.toString());
mRxOperatorsText.append("accept: success :" + foodDetail.toString()+"\n");
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
Log.e(TAG, "accept: error :" + throwable.getMessage());
mRxOperatorsText.append("accept: error :" + throwable.getMessage()+"\n");
}
});

4、结合多个接口的数据更新UI

在实际应用中,我们极有可能会在一个页面显示的数据来源于多个接口,这时候我们的 zip 操作符为我们排忧解难。

zip 操作符可以将多个 Observable 的数据结合为一个数据源再发射出去。

Observable<MobileAddress> observable1 = Rx2AndroidNetworking.get("http://api.avatardata.cn/MobilePlace/LookUp?key=ec47b85086be4dc8b5d941f5abd37a4e&mobileNumber=13021671512")
.build()
.getObjectObservable(MobileAddress.class); Observable<CategoryResult> observable2 = Network.getGankApi()
.getCategoryData("Android",1,1); Observable.zip(observable1, observable2, new BiFunction<MobileAddress, CategoryResult, String>() {
@Override
public String apply(@NonNull MobileAddress mobileAddress, @NonNull CategoryResult categoryResult) throws Exception {
return "合并后的数据为:手机归属地:"+mobileAddress.getResult().getMobilearea()+"人名:"+categoryResult.results.get(0).who;
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
@Override
public void accept(@NonNull String s) throws Exception {
Log.e(TAG, "accept: 成功:" + s+"\n");
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
Log.e(TAG, "accept: 失败:" + throwable+"\n");
}
});

5、间隔任务实现心跳

想必即时通讯等需要轮训的任务在如今的 APP 中已是很常见,而 RxJava 2.x 的 interval 操作符可谓完美地解决了我们的疑惑。

这里就简单的意思一下轮训。

    private Disposable mDisposable;
@Override
protected void doSomething() {
mDisposable = Flowable.interval(1, TimeUnit.SECONDS)
.doOnNext(new Consumer<Long>() {
@Override
public void accept(@NonNull Long aLong) throws Exception {
Log.e(TAG, "accept: doOnNext : "+aLong );
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Long>() {
@Override
public void accept(@NonNull Long aLong) throws Exception {
Log.e(TAG, "accept: 设置文本 :"+aLong );
mRxOperatorsText.append("accept: 设置文本 :"+aLong +"\n");
}
});
} /**
* 销毁时停止心跳
*/
@Override
protected void onDestroy() {
super.onDestroy();
if (mDisposable != null){
mDisposable.dispose();
}
}

三、后记

姑且先讲到这里吧,喜欢的小伙伴别忘了关注和点赞哦~https://github.com/nanchen2251/RxJava2Examples

【知识整理】这可能是最好的RxJava 2.x 入门教程(五)的更多相关文章

  1. 【知识整理】这可能是最好的RxJava 2.x 入门教程(二)

    这可能是最好的RxJava 2.x入门教程系列专栏 文章链接: 这可能是最好的RxJava 2.x 入门教程(一) GitHub 代码同步更新:https://github.com/nanchen22 ...

  2. 【知识整理】这可能是最好的RxJava 2.x 入门教程(三)

    这可能是最好的RxJava 2.x入门教程系列专栏 文章链接: 这可能是最好的RxJava 2.x 入门教程(一) 这可能是最好的RxJava 2.x 入门教程(二) GitHub 代码同步更新:ht ...

  3. 【知识整理】这可能是最好的RxJava 2.x 入门教程(四)

    这可能是最好的RxJava 2.x入门教程系列专栏 文章链接: 这可能是最好的RxJava 2.x 入门教程(一) 这可能是最好的RxJava 2.x 入门教程(二) 这可能是最好的RxJava 2. ...

  4. 【知识整理】这可能是最好的RxJava 2.x 入门教程(一)

    一.前言 这可能是最好的RxJava 2.x入门教程系列专栏 文章链接: 这可能是最好的RxJava 2.x 入门教程(完结版)[强力推荐] 这可能是最好的RxJava 2.x 入门教程(一) 这可能 ...

  5. 这可能是最好的RxJava 2.x 入门教程(一)

    这可能是最好的 RxJava 2.x 入门教程系列专栏 文章链接: 这可能是最好的 RxJava 2.x 入门教程(完结版)[重磅推出] 这可能是最好的RxJava 2.x 入门教程(一) 这可能是最 ...

  6. 【知识整理】这可能是RxJava 2.x 最好的入门教程(一)

    一.前言 RxJava 对大家而言肯定不陌生,其受欢迎程度不言而喻.而在去年的早些时候,官方便宣布,将在一段时间后不再对 RxJava 1.x 进行维护,而在仓库中另辟蹊径,开始对 RxJava 2. ...

  7. 【知识整理】这可能是最好的RxJava 2.x 教程(完结版)

    为什么要学 RxJava? 提升开发效率,降低维护成本一直是开发团队永恒不变的宗旨.近两年来国内的技术圈子中越来越多的开始提及 RxJava ,越来越多的应用和面试中都会有 RxJava ,而就目前的 ...

  8. js事件(Event)知识整理

    事件(Event)知识整理,本文由网上资料整理而来,需要的朋友可以参考下   鼠标事件 鼠标移动到目标元素上的那一刻,首先触发mouseover 之后如果光标继续在元素上移动,则不断触发mousemo ...

  9. Kali Linux渗透基础知识整理(四):维持访问

    Kali Linux渗透基础知识整理系列文章回顾 维持访问 在获得了目标系统的访问权之后,攻击者需要进一步维持这一访问权限.使用木马程序.后门程序和rootkit来达到这一目的.维持访问是一种艺术形式 ...

随机推荐

  1. MyBatis之简单了解Plugin

    MyBatis的Configuration配置中有一个Plugin配置,根据其名可以解释为"插件",这个插件实质可以理解为"拦截器"."拦截器&quo ...

  2. hdu 1711---KMP

    题目链接 Problem Description Given two sequences of numbers : a[1], a[2], ...... , a[N], and b[1], b[2], ...

  3. JBoss7安装、测试、配置和启动以及停止,部署

    转:http://www.hongyanliren.com/2014m01/3013.html 内容概要 JBoss系列三主要目的是演示如何部署应用到JBoss7/WildFly,如下图中描述了部署应 ...

  4. 我和 flow.ci 的第一次亲密接触

    编者按:本文转载自 flow.ci 用户 @君赏 的实践分享,原文链接这里. 这不是第一次听说 flow.ci ,记得当时 fir.im 新出这个服务的时候,我也是心情十分激动的去尝试,结果是只支持安 ...

  5. Bash+R: howto pass parameters from bash script to R(转)

    From original post @ http://analyticsblog.mecglobal.it/analytics-tools/bashr/ In the world of data a ...

  6. Javascript加载执行顺序

    本文主要内容 一.不同位置的script标签执行顺序 二.document.ready和window.onload的区别 一.不同位置的script标签执行顺序 整个加载的过程从解析头部开始,比如ht ...

  7. jquery表单序列化

    $(function(){ $('#send').click(function(){ $.ajax({ type: "GET", url: "test.json" ...

  8. [github项目]基于百度地图二次开发实现的车辆监管(包含车辆定位、车辆图片和方向控制,电子围栏,图形绘制等功能)前端实现(不包含后端实现)

    前言:基于百度地图javascript版本开发,百度地图中所用的key已承诺仅用于测试,不用于商业用途 注:本文所有代码可以到github上进行下载,github地址:http://map.eguid ...

  9. 9个常用iptables配置实例

    iptables命令可用于配置Linux的包过滤规则,常用于实现防火墙.NAT.咋一看iptables的配置很复杂,掌握规律后,其实用iptables完成指定任务并不难,下面我们通过具体实例,学习ip ...

  10. A comparison of local caches (2) 【本地缓存之比较 (2)】

    接上一篇: A comparison of local caches (1) [本地缓存之比较 (1)] This article will compare the asynchronous loca ...