RxAndroid 的基本使用
1.基本概念
Rx是RxJava针对Android的定制版本。这个版本中通过增加最少的类使在Android应用中编写响应式组件简单而且无障碍,特别之处在与它还提供了一个Scheduler,可以在主线程或任何给定的Handler上进行调度。Rx编程方式基于三个基本概念:观察者模式、迭代器模式、函数式编程。下面通过几个例子,描述在Android上面Rx方式进行编程的基本使用。
2.观察者模式
在Android Studio里面引入rxjava和rxandroid两个依赖包后,我们就可以进行rx开发了:
compile 'io.reactivex:rxjava:1.1.6'
compile 'io.reactivex:rxandroid:1.2.1'
Rx的两个核心类是Observable(被观察者)和Observer(观察者)。前者发送数据和消息,后者处理数据和消息。Observer收到数据时,它的onNext()方法会被调用,数据在方法的参数里面被传入。另外Observer还有onComplete()方法,来表示Observable的数据发送完毕,而onError()则表示数据发送过程中出错。
先看一个例子。假设要发送一系列的http请求,这些请求的相对路径我们已经有了:“/home”, “/productList”, “/productDetail”,但是发送的时候需要使用绝对路径,因此可以这样拼接一下:
String[] urls = {"/home", "/productList", "/productDetails"};
for (String url : urls) {
sendHttpRequest(BASE_URL + url);
}
如果用RxAndroid写的话,可以这样做:
Observable observable = Observable
.just("/home", "/productList", "/productDetails");
Observer<String> observer = new Observer<String>() {
public void onCompleted() {}
public void onError(Throwable e) {}
public void onNext(String s) {
sendHttpRequest(BASE_URL + url);
}
};
observable.subscribe(observer);
单从代码量来讲,RxAndroid似乎是把事情搞复杂了。这个我们先不讨论。先看看RxAndroid代码的基本逻辑:
1)创建一个Observable,它发送了一系列的数据。
2)创建一个Observer,它的onNext()方法会接收到数据。另外两个方法暂时不感兴趣,不作任何处理。
3)Observer订阅Observable。Subscribe方法将两者关联起来。
实际上,上述代码还有一种简单的写法:
Observable
.just("/home", "/productList", "/productDetails")
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
sendHttpRequest(BASE_URL + url);
}
});
因为对事件结束和事件出错不感兴趣,所以我们可以用Action1类来接收和处理数据。但是过程是一样的:发送数据;订阅;接收数据。
3.异步编程
RxAndroid一个最重要的功能就是简化异步编程。e.g.假设你要读取手机里面的所有联系人,然后在UI上用一个ListView把联系人一个个显示出来,可以这样做:
Observable
.fromCallable(new Callable<Cursor>() {
@Override
public Cursor call() throws Exception {
return queryContacts(); // ①
}
})
.subscribeOn(Schedulers.io()) // 设置I
.observeOn(AndroidSchedulers.mainThread()) // 设置II
.subscribe(new Action1<Cursor>() {
@Override
public void call(Cursor contacts) {
showContacts(); // ②
}
});
这里面,queryContacts涉及到IO操作,因此,需要保证它在一个异步线程里面运行。因此,使用了一个Callable对象,来包裹这个IO操作。Callable的call()方法只有在Observable被订阅后才会执行,并且你可以指定call()方法在哪个线程里面执行。我们使用了subscribeOn(Schedulers.io()), 指定Callable.call()方法在一个单独的IO线程里面执行。但是一旦加上这一行,后面的Action1.call()方法也会在异步线程里面执行,所以还得加上一句observeOn(AndroidSchedulers.mainThread())
,它的作用是让Action1.call()在主线程里面执行。
总结一下,假设当前线程为主线程,以下表格列出来了不同情况下代码①和代码②的线程关系。
|
代码① |
代码② |
|
|
设置Ⅰ和Ⅱ都没有的情况 |
主线程执行 |
主线程执行 |
|
设置Ⅰ |
异步IO线程执行 |
异步IO线程执行 |
|
设置Ⅰ + 设置Ⅱ |
异步IO线程执行 |
主线程执行 |
在Android里面,异步编程还有一个需要注意的地方,那就是Activity销毁之后,需要停止线程。上面的代码,在Activity.onDestroy()里面,应该停止对Observable的订阅,因为这个时候,线程可能还没执行完毕,这个时候,需要停止对数据的接收。只要停止订阅就可以了,如何停止订阅呢?实际上,上面的代码最终会返回一个Subscription对象:
Subscription subscription = Observable
.fromCallable(..
.subscribeOn(...
.observeOn(...)
这个Subscription对象代表Observable和Observer的一个连接,我们可以这样来停止订阅:
subscription.unsubscribe();
4.操作符(Operator)
在Observer接收到数据前,可以用操作符可以对Observable发出的数据进行转换。还是上面的例子,假设我们在读取联系人后,要根据联系人的名字生成对应的图片,并且,只列出来姓“李”的联系人,我们可以这样做:
Observable
.fromCallable(new Callable<List<File>>() {
@Override
public Cursor call() throws Exception {
return queryContacts(); // ①
}
})
.map(new Func1<Cursor, List<Contact>() {
@Override
public List<Contact> call(Cursor c) {
return getContactsFromCursor(c); // ④
}
})
.flatMap(new Func1<List<Contact>, Observable<Contact>>() {
@Override
public Observable<Contact> call(List<Contact> contacts) {
return Observable.from(contacts); // ②
}
})
.filter(new Func1<Contact, Boolean>() {
@Override
public Boolean call(Contact c) {
return c.getName().startWith("黄"); // ③
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Contact>() {
@Override
public void call(Contact c) {
showContact(c);
}
});
这段代码里面展示了RxAndroid里面三个常用的操作符,flatMap,filter,map. 前面提到,操作符是在Observer接收到数据之前,对Observable发送的数据进行中间转换。以下是转换过程:
步骤①:此时所有数据都存在一个List里面,如果没有其它操作符的转换,我们最后的Observer也会接收到一个List,也就是说,数据是一次性接收到的。但是,需求是图片是分批、一个一个传过来,因此我们需要将list展开。
步骤② flatMap操作符:这一步实际上是将上一步的Observable转换成了一个新的Observable,转换过程中,输入是List<Contact>类型,输出是Contac类型。发送的数据并没有减少,只是发送方式变了,由一次性发送变成一个个发送。
步骤③ filter操作符:这一步对数据进行了过滤,只有姓李的联系人才会被发送到Observer
步骤④ map操作符:这一步对数据进行了类型转换。我们读Cursor,并生成联系人列表。输入的是Cursor,输出的是List<Contact>。
5.Subject
回顾我们第一个例子,我们的Observer,Observable都是临时对象,数据处理完毕之后,它们也完成了自己的生命周期。假设我们再来一批数据,并且数据处理流程一样,我们是不是需要再创建新的Observable和Observer?代码重新写一遍?
Observable
.just("/home", "/productList", "/productDetails")
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
}
});
使用RxAndroid里面的Subject对象可以解决这个问题。Subject既是Observable,也是Observer,可以把它想象成一个管道:可以随时往一端输入数据,经过中间处理后,另一端处理输出。考虑这样一个场景,你在联系人列表里面进行搜索,你在搜索框里面每输入一个字符,联系人都会实时重新查询一遍。这里面就是一个标准的Rx流程:输入->处理(查询)->输出。只不过输入条件会不断的变,然后这个流程需要重复执行。
mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String query) {
mSubject.onNext(query);
return true;
}
});
mSubject = PublishSubject.create();
mSubject
.debounce(400, TimeUnit.MILLISECONDS)
.observeOn(Schedulers.io())
.map(new Func1<String, List<Contact>>() {
@Override
public List<Contact> call(String s) {
return queryContacts(s);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Contact>() {
@Override
public void call(Contact contact) {
addContactToList(contact);
}
});
这里面,searchView每次输入有变化时,都会重新查询一遍联系人数据库。因此我们用一个全局变量保存subject,这样可以调用subject.onNext()多次发送数据。这里有两个需要说明的地方:
①:我们用了一个debounce方法,它的作用是,当400ms内没有新的数据输入时,subject才会发送数据。因为用户每输入一个字符时,searchView的onQueryTextChange方法都会被触发。而我们不需要这么频繁的查询。
②:我们用了两个observeOn来指定中间操作和最后操作的执行线程。查询的时候,我们需要在异步线程执行,而最终把联系人加入到查询结果列表的时候,我们又需要切换到主线程。
参考文档:
① 从案例学RxAndroid开发:
http://www.infoq.com/cn/articles/RxAndroid-basics
② Rx官网Subject文档:
http://reactivex.io/documentation/subject.html
RxAndroid 的基本使用的更多相关文章
- 对rxandroid的简单理解
最近发现这个rxandroid挺火的,我就研究了一下,还真的挺不错. 首先在说之前可能很多人会和我刚刚学习的时候一样有很多疑问,如: 1:rxandroid是什么东西? 2:rxandroid能干嘛? ...
- RxAndroid/java小记
Rxandroid 作为一个在设计模式中能把MVP发挥的淋漓尽致的框架不去学习感觉真的对不起自己,然后也学点新东西吧,响应式编程,MVP观察者模式,然后使用RxAndroid使我们自己的代码更加简洁 ...
- [Android]基于RxJava、RxAndroid的EventBus实现
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4578699.html Github:https://gith ...
- RxJava 和 RxAndroid 五(线程调度)
对rxJava不了解的同学可以先看 RxJava 和 RxAndroid 一 (基础)RxJava 和 RxAndroid 二(操作符的使用)RxJava 和 RxAndroid 三(生命周期控制和内 ...
- RxJava 和 RxAndroid 四(RxBinding的使用)
对Rxjava不熟悉的同学可以先看我之前写的几篇文章 RxJava 和 RxAndroid 一 (基础) RxJava 和 RxAndroid 二(操作符的使用) RxJava 和 RxAndroid ...
- RxJava 和 RxAndroid 三(生命周期控制和内存优化)
rxjava rxandroid 赵彦军 前言:对Rxjava.Rxandroid不了解的同学可以先看看 RxJava 和 RxAndroid RxJava 和 RxAndroid 二(操作符的使用) ...
- RxJava 和 RxAndroid 二(操作符的使用)
前言:对Rx不了解的朋友可以先看我的第一篇博文 RxJava 和 RxAndroid 一 (基础),是对Rxjava的基本介绍 1.merge操作符,合并观察对象 List<String> ...
- RxJava 和 RxAndroid 一 (基础)
1.RxJava 项目地址 https://github.com/ReactiveX/RxJava 2.RxAndroid 项目地址 https://github.com/ReactiveX/R ...
- Rxjava, RxAndroid, Retrofit 等库的使用
RxJava的基本用法: 关于 unSubscribe() 的调用问题: There is no need to unsubscribe in onCompleted. Take a look at ...
- RxAndroid结合Retrofit,看看谁才是最佳拍档!
这篇博文酝酿好久了,今天终于下定决心开始写!RxAndroid和Retrofit都算是当下非常流行的Android开发框架,这两个框架光是单独使用就已经爽歪歪了,那么将RxAndroid和Retrof ...
随机推荐
- Poj1733 Parity Game(带权并查集)
题面 Poj 题解 反正只要你判断是否满足区间的奇偶性,假设每一位要么是\(1\)要么是\(0\)好了. 假设有\(S\)的前缀和为\(sum[]\),则有: 若\(S[l...r]\)中有奇数个\( ...
- 2017icpc 乌鲁木齐网络赛
A .Banana Bananas are the favoured food of monkeys. In the forest, there is a Banana Company that pr ...
- [SRM686]CyclesNumber
题意:求$n$个数的所有排列形成的轮换个数的$m$次方之和 我以前只知道这是GDKOI的题,今天在ckw博客上发现它是TC题...原题真是哪里都有... 就是求$\sum\limits_{i=1}^n ...
- 【推导】Codeforces Round #432 (Div. 2, based on IndiaHacks Final Round 2017) B. Arpa and an exam about geometry
题意:给你平面上3个不同的点A,B,C,问你能否通过找到一个旋转中心,使得平面绕该点旋转任意角度后,A到原先B的位置,B到原先C的位置. 只要A,B,C构成等腰三角形,且B为上顶点.那么其外接圆圆心即 ...
- 【动态规划】Codeforces Round #417 (Div. 2) B. Sagheer, the Hausmeister
预处理每一层最左侧的1的位置,以及最右侧的1的位置. f(i,0)表示第i层,从左侧上来的最小值.f(i,1)表示从右侧上来. 转移方程请看代码. #include<cstdio> #in ...
- bzoj 1585: [Usaco2009 Mar]Earthquake Damage 2 地震伤害
1585: [Usaco2009 Mar]Earthquake Damage 2 地震伤害 Description Farmer John的农场里有P个牧场,有C条无向道路连接着他们,第i条道路连接着 ...
- Problem C: 程序改错(递归函数):数字转字符
Description 下面程序中“/ ***** N ***** /”的下一行中有错误,请改正(注意:不得加行.减行.加句.减句,否则后果自负). 该程序功能:用递归法将一个六位整数n转换成字符串, ...
- UNIX域套接字连接mysql
用户可以在配置文件中指定套接字文件的路径,如--socket=/data/mysql/mysql.sock [root@localhost ~]# mysql -uroot -p123456 -S / ...
- 让Code First下的数据库的迁移更加简单
Code First给我们的程序开发带了很多便利,之前的版本中一个比较不大方便的地方是数据库迁移,麻烦不说,往往还和上下文相关,在不同的版本之间的数据库进行迁移还很容易失败,并且一旦失败还不大容易找到 ...
- WPF Interaction框架简介(一)——Behavior
在WPF 4.0中,引入了一个比较实用的库——Interactions,这个库主要是通过附加属性来对UI控件注入一些新的功能,除了内置了一系列比较好用的功能外,还提供了比较良好的扩展接口.本文这里简单 ...