写点什么

从案例学 RxAndroid 开发(上)

  • 2016-03-28
  • 本文字数:4017 字

    阅读完需:约 13 分钟

原文链接: RxAndroid Basics: Part 1

如果你在阅读这篇文章,相信你一定很想了解 RxJava 以及如何在 Android 应用中使用它。可能你已经见过 RxJava 的代码了,但仍然有些疑惑,愿你能在这篇文章里找到答案。

当我第一次使用 RxJava 的时候我只是在照搬代码,这些代码能跑起来,但是我对 RxJava 的基础部分仍然存在误解,而且我找不到好的源码来学习。所以为了理解 RxJava,我不得不一点一点学习,踩了不少坑。

为了不让你把我踩过的坑再踩一遍,我会基于我的学习成果写一些例子出来,目的就是让你能够对 RxJava 有足够的了解,并能在你的 Android 应用中使用它。

源码可以在这里找到。在每个例子的开始,我会写清每个代码段是属于哪个Activity 的。我会将本文分为两个部分,在第一部分里,我会着重讲解如何用RxJava 异步加载数据;在第二部分里,我会探索一些更高级的用法。

几个概念

在开始说代码之前,先澄清几个概念。RxJava 最核心的东西就是Observable 和Observer。Observable 会发出数据,而与之相对的Observer 则会通过订阅Observable 来进行观察。

Observer 可以在 Observable 发出数据、报错或者声明没有数据可以发送时进行相应的操作。这三个操作被封装在 Observer 接口中,相应的方法为 onNext(),onError() 和 onCompleted()。

明确了这些概念以后,让我们来看一些例子。

案例 1:基础

现在要写一个用来展示一个颜色列表的 Activity。我们要写一个能发送一个字符串列表、然后结束的 Observeable。而后我们会通过这个字符串列表来填充颜色列表,这里要使用到 Observable.just() 方法。由这个方法创建的 Observable 对象的特点是:所有 Observer 一旦订阅这个 Observable 就会立即调用 onNext() 方法并传入 Observable.just() 的参数,而后因为 Observable 没有数据可以发送了,onComplete() 方法会被调用。

Observable<List<String>> listObservable = Observable.just(getColorList());注意这里的 getColorList() 是一个不耗时的方法。虽然现在看来这个方法无足轻重,但一会我们会回到这个方法。

下一步,我们写一个 Observer 来观察 Observable。

复制代码
listObservable.subscribe(new Observer<List<String>>() {
@Override
public void onCompleted() { }
@Override
public void onError(Throwable e) { }
@Override
public void onNext(List<String> colors) {
mSimpleStringAdapter.setStrings(colors);
}
});

而后神奇的事情就发生了。如我刚才所说,一旦通过 subscribe() 方法订阅 Observable,就会发生一系列事情:

  1. onNext() 方法被调用,被发送的颜色列表会作为参数传入。
  2. 既然不再有数据可以发送(我们在 Observable.just() 中只让 Observable 发送一个数据),onComplete() 方法会被调用。

请记住:通过 Observable 被订阅后的行为来区分它们

在这个例子中我们不关心 Observable 何时完成数据的传输,所以我们不用在 onComplete() 方法里写代码。而且在这里不会有异常抛出,所以我们也不用管 onError() 方法。

写了这么多你可能觉得很多余,毕竟我们本可以在 adapter 中直接设置作为数据源的颜色列表。请带着这个疑问,和我看下面这个更有趣一些的例子。

案例 2:异步加载

在这里我们要写一个显示电视剧列表的 Activity 。在 Android 中 RxJava 的主要用途就在于异步数据加载。首先让我们写一个 Observable:

复制代码
Observable<List<String>> tvShowObservable = Observable.fromCallable(new Callable<List<String>>() {
@Override
public List<String> call() {
return mRestClient.getFavoriteTvShows();
}
});

在刚才的例子中,我们使用 Observable.just() 来创建 Observable,你可能认为在这里可以通过 Observable.just(mRestClient.getFavoriteTvShows()) 来创建 Observable。

但在这里我们不能这么做,因为 mRestClient.getFavoriteTvShows() 会发起网络请求。如果在这里我们使用 Observable.just(),mRestClient.getFavoriteTvShows() 会被立即执行并阻塞 UI 线程。

使用 Observable.fromCallable() 方法有两点好处:

  1. 获取要发送的数据的代码只会在有 Observer 订阅之后执行。
  2. 获取数据的代码可以在子线程中执行。

这两点好处有时可能非常重要。现在让我们订阅这个 Observable。

复制代码
mTvShowSubscription = tvShowObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<String>>() {
@Override
public void onCompleted() { }
@Override
public void onError(Throwable e) { }
@Override
public void onNext(List<String> tvShows){
displayTvShows(tvShows);
}
});

让我们一个方法一个方法地来看这段代码。subscribeOn 会修改我们刚刚创建的 Observable。在默认情况下 Observable 的所有代码,包括刚才说到的只有在被订阅之后才会执行的代码,都会在执行 subscribe() 方法的线程中运行。而通过 subscribeOn() 方法,这些代码可以在其他线程中执行。但具体是哪个线程呢?

在这个例子中我们让代码在"IO Scheduler"中执行(Schedulers.io())。现在我们可以只把 Scheduler 当做一个可以工作的子线程,这个描述对于现在的我们已经足够了,不过这其中还有更深层次的内容。

不过我们的确遇到了一个小障碍。既然 Observable 会在 IO Scheduler 中运行,那么它与 Observer 的连接也会在 IO Scheduler 中完成。这就意味着 Observer 的 onNext() 方法也会在 IO Scheduler 中运行,而 onNext() 方法会操作 UI 中的 View,但 View 只能在 UI 主线程中操作。

事实上解决这个问题也很简单,我们可以告诉 RxJava 我们要在 UI 线程中观察这个 Observable,也就是,我们想让 onNext() 方法在 UI 线程中执行。这一点我们可以通过在 observeOn() 方法中指定另一个 Scheduler 来完成,在这里也就是 AndroidSchedules.mainThread() 所返回的 Scheduler(UI 线程的 Scheduler)。

而后我们调用 subscribe() 方法。这个方法最重要,因为 Callable 只会在有 Observer 订阅后运行。还记得刚才我说 Observable 通过其被订阅后的行为来区分吗?这就是一个很好的例子。

还有最后一件事。这个 mTvShowSubscription 到底是什么?每当 Observer 订阅 Observable 时就会生成一个 Subscription 对象。一个 Subscription 代表了一个 Observer 与 Observable 之间的连接。有时我们需要操作这个连接,这里拿在 Activity 的 onDestroy() 方法中的代码举个例子:

复制代码
if (mTvShowSubscription != null && !mTvShowSubscription.isUnsubscribed()) {
mTvShowSubscription.unsubscribe();
}

如果你与多线程打过交道,你肯定会意识到一个大坑:当 Activity 执行 onDestroy() 后线程才结束(甚至永不结束)的话,就有可能发生内存泄漏与 NullPointerException 空指针异常。

Subscription 就可以解决这个问题,我们可以通过调用 unsubscribe() 方法告诉 Observable 它所发送的数据不再被 Observer 所接收。在调用 unsubscribe() 方法后,我们创建的 Observer 就不再会收到数据了,同时也就解决了刚才说的问题。

说到这里难点已经过去,让我们来总结一下:

  • Observable.fromCallable() 方法可以拖延 Observable 获取数据的操作,这一点在数据需要在其他线程获取时尤其重要。
  • subscribeOn() 让我们在指定线程中运行获取数据的代码,只要不是 UI 线程就行。
  • observeOn() 让我们在合适的线程中接收 Observable 发送的数据,在这里是 UI 主线程。
  • 记住要让 Observer 取消订阅以免 Observable 异步加载数据时发生意外。

案例 3:使用 Single

这次我们还是写一个展示电视剧列表的 Activity ,但这次我们走一种更简单的风格。Observable 挺好用的,但在某些情况下过于重量级。比如说,你可能一经发现在过去的两个方法中我们只是让 Observable 发送一个数据,而且我们从来也没写过 onComplete() 回调方法。

其实呢,Observable 还有一个精简版,叫做 Single。Single 几乎和 Observable 一模一样,但其回调方法不是 onComplete()/onNext()/onError(),而是 onSuccess()/onError()。

我们现在把刚才写过的 Observable 用 Single 重写一遍。首先我们要创建一个 Single:

复制代码
Single<List<String>> tvShowSingle = Single.fromCallable(new Callable<List<String>>() {
@Override
public List<String> call() throws Exception {
mRestClient.getFavoriteTvShows();
}
});

然后订阅一下:

复制代码
mTvShowSubscription = tvShowSingle
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleSubscriber<List<String>>() {
@Override
public void onSuccess(List<String> tvShows) {
displayTvShows(tvShows);
}
@Override
public void onError(Throwable error) {
displayErrorMessage();
}
});

这段代码和刚才很像,我们调用 subscribeOn() 方法以确保 getFavoriteTvShows() 在子线程中执行。而后我们调用 observeOn() 以确保 Single 的数据被发送到 UI 线程。

但这次我们不再使用 Observer,而是使用一个叫 SingleSubscriber 的类。这个类和 Observer 非常像,只不过它只有上述两个方法:onSuccess() 和 onError()。SingleSubscriber 之于 Single 就如 Observer 之于 Observable。

订阅一个 Single 的同时也会自动创建一个 Subscription 对象。这里的 Subscription 和案例 2 中没有区别,一定要在 onDestroy() 中解除订阅。

最后一点:在这里我们添加了处理异常的代码,所以如果 mRestClient 出了问题,onError() 就会被调用。建议你亲手写一个案例玩一玩,体验一下有异常时程序是怎么运行的。

结语

第一部分就说这么多了,希望这几个案例能对你有所帮助,不要忘了看看第二部分中更加高级的使用方法哦。

2016-03-28 04:2820153

评论

发布
暂无评论
发现更多内容

直播教学!20 分钟开发可视化「智能门铃」丨RTE 开发实战课 • 第一期

声网

最佳实践 直播 RTC 声网

URule规则引擎

规则引擎 java

Last Week in Milvus

Zilliz

Milvus Zilliz 向量数据库

真香!阿里最新出品Java面试核心讲(终极版),Github已星标50K

程序员小毕

Java 程序员 面试 后端 架构师

K8S部署应用详解

tiandizhiguai

NineData x 阿里云 正式上线

NineData

数据库 阿里云 数据迁移 数据管理 NineData

可插拔组件设计机制—SPI

京东科技开发者

spi Java】 JavaSPI 企业号 3 月 PK 榜

OPPO、京东云 loT 项目数据架构改造,数据处理痛点这样破解

TDengine

tdengine 数据架构 时序数据库 用户案例 loT

如何在 macOS 中互换 Control 和 Command 键

理理

mac教程 Control键 Command 键

3D摄影棚布光工具Set A Light 3D Studio

Rose

Mac软件 Set A Light 3D Studio 3D摄影棚布光工具

面面俱到!四面阿里拿offer后,才发现师哥给的面试笔记有多强大

做梦都在改BUG

Java java面试 Java八股文 Java面试题 Java面试八股文

喜讯|百度入选“移动互联网APP产品安全漏洞治理”优秀案例

百度安全

AntDB数据库助力中国移动华南中心计费项目

亚信AntDB数据库

AntDB 国产数据库 aisware antdb AntDB数据库 企业号 3 月 PK 榜

金三突击面试,收获6个Offer,原来面试还能这么简单!

做梦都在改BUG

Java java面试 Java八股文 Java面试题 Java面试八股文

ElasticSearch必知必会-Reindex重建索引

京东科技开发者

elasticsearch 索引 ES 集群 企业号 3 月 PK 榜

解决 Parallels Desktop 虚拟机不能连网的问题

理理

Parallels Desktop 虚拟机 PD虚拟机不能联网 PD常见问题

通过Flutter实现一个能在多端运行的扫雷游戏

编程的平行世界

flutter 前端 游戏 移动端 扫雷

科技助力金融转型 阿里云联合中国信通院在京举办未来金融思享会

阿里云云效

DevOps 数字化转型 金融 BizDevOps 业技融合

真的有那么丝滑吗?面试阿里(Java岗)从投简历到面试再到入职

做梦都在改BUG

Java java面试 Java八股文 Java面试题 Java面试八股文

企业不想走弯路,不如试试低代码开发

引迈信息

低代码 低代码开发 JNPF

SpringBoot项目就连创建目录都让人抓狂

做梦都在改BUG

Java Spring Boot 框架

从稀疏表征出发、召回方向的前沿探索

百度Geek说

召回 预训练模型 稀疏矩阵 企业号 3 月 PK 榜

React数字滚动组件 numbers-scroll

观纵科技

JavaScript 前端监控 React

亿级用户中心的设计与实践

做梦都在改BUG

Java 服务架构 亿级流量 用户中心

业务架构那点事(2)如何通过高层访谈获取企业战略信息?

涛哥 数字产品和业务架构

业务架构 访谈

Mac教程:如何开启任何来源选项

理理

Mac 苹果电脑 任何来源

CleanMyMac4.20专业的mac清理软件

茶色酒

CleanMyMac4.20

使用Assembly打包和部署Spring Boot工程

做梦都在改BUG

Java spring Spring Boot assembly 框架

VPN客户端Shimo mac版使用教程:如何创建新的 VPN 帐户?

Rose

vpn mac系统 Shimo下载 Shimo教程

交易系统之数据库弱依赖解决方案

京东科技开发者

数据库 高并发 灾备 db 企业号 3 月 PK 榜

Acrobat Pro DC 2023发布,有哪些新的改进?

Rose

adobe pdf编辑器 Acrobat Pro DC 2023

从案例学RxAndroid开发(上)_移动_Kurtis Nusbaum_InfoQ精选文章