这几天一直看《激荡三十年》,感觉非常不错。这篇文章本身并不想写,总觉得没什么含量,后面写着写着,发现其中的有些点还是非常有意思的,因此这篇文章的重点在rxjava使用场景说明及rxjava内存优化上。rxjava的使用场景更多的取决于我们所面临的业务以及当前rxjava的一些操作符是否提供了对业务的支持,而对于内存优化上,则是要求我们尽可能快的解除订阅关系。
另外本文对RxBinding,RxLifecycle的介绍并未做过多的深入,原因在于这两个库无论是从实现还是从使用角度来说都是比较简单的,我们完全可以自行书写类似的库。
话不多说,步入正题。
引入RxBinding
在引入RxBinding之前首先要弄明吧RxBinding是什么?。
所谓的RxBinding是用来为界面元素绑定事件的,比如为Buttong设置点击事件等。浏览其源码,不难发现其实现原理也是通过包装原有事件实现的。
为什么要引入RxBinding?使用RxJava一定要引入RxBinding么?
首先很确定的说使用RxJava不要求你一定要使用RxBinding,大部分情况下没必要用。这里之所以要谈RxBinding一方面是完善我们的知识体系,看看响应式编程的思想是如何应用在android界面元素上,另一方面是看看RxBinding能够有效的解决什么问题?
RxBinding提供了和RxJava一致的api体验,更重要的是它更好的符合RxJava做法:通过将事件转化为Observable对象,最终可以利用RxJava一系列操作符对其处理,最典型的使用场景是界面防抖动,这点我们在rxjava真实应用场景中做详细的介绍。
关于如何使用RxBinding,直接参见RxBinding项目说明即可:https://github.com/JakeWharton/RxBinding。
rxjava真实应用场景
在上一篇文章中为解决异常问题,我们引入了rxjava的支持。接下来我们来看看rxjava在实际工程中的显著应用。
场景零:线程切换
rxjava引入让使得线程切换更加的容易,几行代码就可以搞定。RxAnroid的引入更是让我们非常容易的能够切换到UI线程。可以说,引入RxJava,就放弃古老而沉重的AsyncTask吧(初学者还是要学AsyncTask的)。最典型的就是从网络中获取数据,然后在更新界面,很显然获取数据操作需要发生在子线程,更新UI操作发声明在主线程。这里我们以模拟从数据库中获取联系人操作为例:
private void getConcactFromDB() {
Observable.create(new Observable.OnSubscribe<List<String>>() {
@Override
public void call(Subscriber<? super List<String>> subscriber) {
//模拟从数据库中获取数据
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
list.add("user name:" + i);
}
//模拟耗时操作
SystemClock.sleep(5000);
subscriber.onNext(list);
subscriber.onCompleted();
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<String>>() {
@Override
public void call(List<String> list) {
Log.d("MainActivity", "更新界面:" + list.size());
}
});
}
场景一:接口依赖(flatmap)
目前大部分服务端接口设计都是通过用户名和密码登录获取access token,后面其他api的请求都是借助该token。对于需要注册功能的产品来说,我们经常面对这样的问题:使用用户名和密码登录成功后,保存服务器返回的access token,再调用服务端接口获取用户的详情信息。不难发现,这里获取用户详情的请求依赖登录请求.我们先来看传统方法是如何解决这问题:
private void handleLogin2(LoginPost post) {
ApiFactory.getBaseApi().login(post).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BaseSubscriber<Result<Token>>(this){
@Override
public void onNext(Result<Token> tokenResult) {
if (tokenResult.isOk()) {
Token data = tokenResult.getData();
String token = data.getToken();
//保存token操作 ApiFactory.getUserApi().getUserProfile(token).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BaseSubscriber<Result<User>>(LoginActivity.this){
@Override
public void onNext(Result<User> userResult) {
//处理用户信息
}
@Override
public void onError(Throwable e) {
//处理错误
}
});
}
}
@Override
public void onError(Throwable e) {
//处理错误
}
});
}
开始时,我们大部分人会写出类似以上的代码。当然,这实现了我们想要的逻辑,但当你仔细思考的时候,会发现几个问题:
- 回调嵌套看起来令人疑惑。由于我们在大多数情况下是线性思维,那么此时当你看到
onNext(Result<Token> tokenResult)
中又去嵌套处理获取用户信息的接口你的思维不得不跳跃一下。 - 登录功能的异常处理点被分隔了,使我们不得不写出冗余的代码。
- 多次线程开销好像可以被进一步优化。
实际上这三个问题的根本原因在于我们在实现登录功能的时候是以方法作为最小单位,而不是以登录逻辑为最小单位,因此看起不是那么的连贯。现在来看看我们应该怎么样让上面的代码具有连贯性:
private void handleLogin(LoginPost post) {
ApiFactory.getBaseApi().login(post).flatMap(new Func1<Result<Token>, Observable<Result<User>>>() {
@Override