前言
最近研究很火的开源库RxJava,看过很多国内android工程师写的介绍。例如,Flipboard的扔物线同学写的《给 Android 开发者的 RxJava 详解》,讲解得非常通俗易懂。不过我觉得对于初学者,还是不够直观(可能是我比较蠢)。
本文重点将一些常用场景罗列出来,让大家简单地入门RxJava。
RxJava是什么
RxJava的github官网第一句说到:
“RxJava is a Java VM implementation of Reactive Extensions: a library for composing asynchronous and event-based programs by using observable sequences.”
本人理解:“RxJava是一个实现java响应式编程的库,让异步事件以序列的形式组织代码。”
引入RxJava
在build.gradle
在dependencies
加入
dependencies {
...
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.0'
}
异步网络请求:
场景:异步网络请求一个User数据,并在TextView展示。
平常代码:
TextView textView = ...;
Map<String, String> params = new HashMap<>();
params.put("user_id", userid);// 请求参数
UserHttp client = new UserHttp();
client.post("http://server.com/user", params, new CallBack<String>() { // 异步请求
@Override
protected void onSuccess(String result) { // 在UI线程回调
// 返回的字符串(通常是一个json),解析成User对象
User user = parse(result);
textView.setText(user.getName());
}
});
大概就是这样子了吧,当然一般都会再封装一下。
用RxJava大概是这样子:
TextView textView = ...;
Observable<String> observable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(final Subscriber<? super String> subscriber) {// 下面subscribeOn(Schedulers.newThread()) 把这方法设定在新线程回调
Map<String, String> params = new HashMap<>();
params.put("user_id", userid);// 请求参数
UserHttp client = new UserHttp();
Response response = client.post("http://kkmike999.com/user", params);// 同步请求
if (response.status == 200) { // 请求成功
String result = response.getResult();
subscriber.onNext(result);
subscriber.onCompleted();
} else {
// 请求失败
subscriber.onError(new Throwable(response.getMessage()));
}
}
})
.subscribeOn(Schedulers.newThread()) // 设置call(...)方法,在新线程回调;
// 可封装得更美观 Observable<String> observable = UserHttp.create(userid);
observable
.observeOn(AndroidSchedulers.mainThread())// 让下面onNext()、onError()、onComplete()在UI线程(主线程)回调
.subscribe(new Subscriber<String>() {
@Override
public void onNext(String result) { // 上面 subscriber.onNext(result)在这里回调
// 返回的字符串(通常是一个json),解析成User对象
User user = parse(result);
textView.setText(user.getName());
}
@Override
public void onError(Throwable e) {} // 上面subscriber.onError(new Throwable(msg))在这里回调
@Override
public void onCompleted() {}
});
“这是什么鬼,代码量不是多了吗?”(心中千万只草泥马奔腾......)
在这么简单的场景上,使用RxJava代码量确实会变多。但是(掩饰),请求User、解析User数据的逻辑拆开,也可设定什么事件在什么线程执行。subscribeOn(Schedulers.newThread())
设定下面call(subscriber<String>)
在新线程回调,observeOn(AndroidSchedulers.mainThread())
设定本行下面的函数onNext
、onError
、onCompleted
都在主线程执行。一行代码切换事件回调的线程,是不是很牛逼?
关于subscribeOn()
、observeOn()
、Schedulers
的解释,参考《给 Android 开发者的 RxJava 详解》。
异步请求&处理数据,主线程UI操作
场景条件苛刻点:User user = parse(result);
是一个耗时操作,要求异步线程处理,setText()在主线程操作。这时应该怎么处理?
平常代码:
client.post("http://server.com/user", params, new CallBack<String>() { // 异步请求
@Override
protected void onSuccess(String result) { // 在UI线程回调
// 异步处理
new Thread(new Runnable(){
@Override
public void run(){
User user = parse(result);
// 主线程
runOnUiThread(new Runnable(){
@Override
public void run(){
textView.setText(user.getName());
}
});
}
}).start();
}
});
蛋蛋疼的感觉出来木有? 八百个回调!!!这种状况,业界称为“Callback Hell(回调地狱)”.
用RxJava让你爽爽:
Observable<String> observable = Observable.create(new Observable.OnSubscribe<String>(){
call(){...} // Thread1回调
})
.subscribeOn(Schedulers.newThread()) // 设置 新线程Thread1回调call(...)
observable
.map(new Func1<String, User>() {
@Override
public User call(String result) { // Thread1回调
User user = parse(result);
return user;
}
})
.observeOn(AndroidSchedulers.mainThread()) // 切换回主线程
.subscribe(new Subscriber<User>() {
@Override
public void onNext(User user) { // 主线程
textView.setText(user.getName());
}
});
没有多重回调了!
map(new Func1<String, User>() {...}
做了转换工作,把string
转成User
,交给Subscriber<User>
处理。
事件流
RxJava最大特点,就是以“事件流”(前言中“序列的形式”)写代码。
上文中 事件1 网络请求call(Subscriber<String>...)
、事件2 json->User map(new Func1<String,User>...)
、事件3 textView.setText()
,是事件先后顺序,自上而下写代码,而不像平常代码那样“嵌套回调”。
随着业务日益复杂,代码会变得臃肿。多数情况下工程师会把代码挪到Utils、Manager、Controller里面,即使这样,由于多线程、并发,“嵌套回调”无可避免。使用RxJava可以解决“Callback Hell”,让业务逻辑非常清晰,尽管读代码的人不知道内部怎么实现,但清楚每一个事件发生的顺序,看代码效率提高,做测试也容易。
这就是为什么要用RxJava的最大原因。
值得注意,事件流在调用subscribe()
之后,才开始工作。
多线程处理
还是之前的场景:....先在Thread1把string
转成JSONObject
,再在Thread2将JSONObject
转User
... (此处为了演示故意把情景弄复杂)
observable.subscribeOn(Schedulers.newThread()) // 新线程Thread1
.map(new Func1<String,JSONObject>(){
@Override
public JSONObject call(String result) { // 在Thread1运行
return new JSONObject(result);
}
})
.subscribeOn(Schedulers.newThread()) // 切换新线程Thread2
.map(new Func1< JSONObject, User>() {
@Override
public User call(JSONObject json) { // 在Thread2运行
User user = parse(json); // 与之前的parse()函数不一样了
return user;
}
})
.observeOn(AndroidSchedulers.mainThread()) // 切换回主线程
.subscribe(new Subscriber<User>() {
@Override
public void onNext(User user) { // 主线程
textView.setText(user.getName());
}
});
好屌对吧! 还有flatMap()
可以做转换,跟map()
有区别,由于有点复杂,此处不列举flatMap()
。
加入进度条
场景:....在请求开始前,显示进度条,数据处理完后,隐藏进度条...
RxJava:
ProgressDialog progress = ...;
progress.show();
...
.observeOn(AndroidSchedulers.mainThread()) // 规定Subscriber在主线程回调
.subscribe(new Subscriber<List<String>>() { // 主线程
@Override
public void onNext(List<String> strings) {...}
@Override
public void onComplete() {
progress.dismiss();
}
}
onNext
、onComplete
被设置在主线程执行,因此可以执行ui相关代码。
变种观察者模式
RxJava说是观察者,但实际用起来有出入(《给 Android 开发者的 RxJava 详解》有解释为什么),特别是命名会让刚接触的同学混淆。所以我不强调RxJava的观察者模式,大家懂得使用后再体会。
总结
使用RxJava写代码,嵌套的逻辑、各线程回调...都会以‘序列’的形式(通常称为‘流’)表现,你不会在RxJava代码看到嵌套回调(如果有,很可能是使用不当)。‘流’让代码更直观,逻辑按事件处理顺序,一步一步来。读者非常清晰哪个事件在哪个线程执行,这一点至关重要。
刚使用门槛稍高,但随着业务复杂化,RxJava的优势日渐明显。RxJava不一定提供最好的开发模式,但接触之后,会体验到java另一层境界。
感谢
感谢撰写《给 Android 开发者的 RxJava 详解》的抛物线同学,对我学习RxJava有很大帮助。还要业界为RxJava翻译中文文档的、写各种使用文章的同学,你们辛苦了!
demo: https://github.com/kkmike999/RxJavaAsynPostDemo
版权声明:可自由转载,必须写上原文链接or原作者