RX基本思想
rx 本质上来讲不是基于某一种语言的库,而是一种思想。想要搞懂Rx 那么必须要明白它的思想。
其实就是 起点到终点的一系列的事件流,举个例子来说,我们的饮用水,起点是水库的地下水,终点是我们家的水龙头,中间我们可以做很多操作,比如 我们可以净化水等等。。。。 但是无论怎样中间的水流是不会断的, 也就是这个事件是连续的。
再比如我们程序中 点击登陆(起点)---->请求登陆API---->获取响应信息---->登陆成功(终点) 这一系列的事件流就可以用Rx来表示
基本使用
网络请求中最常用的就是Retrofit+RxJava了
- 首先定义APiService
public interface APiService {
// 总数据
@GET("project/tree/json")
Observable<ProjectBean> getProject();
// item数据
@GET("project/list/{pageIndex}/json")
Observable<ProjectItem> getProjectItem(@Path("pageIndex") int pageIndex,
@Query("cid") int cid);
}
其实也就是正常的Retrofit使用流程 只不过 把返回类型变成 Observable<T>了 表示起点是这里了
2.请求数据
// 获取网络API
api.getProject()
.subscribeOn(Schedulers.io()) // 上面 异步
.observeOn(AndroidSchedulers.mainThread()) // 下面 主线程
.subscribe(new Observer<ProjectBean>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(ProjectBean projectBean) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
第一行不用说,就是Retrofit的用法 获取起点 , 那么终点呢?终点自然就是 new Observer <ProjectBean>(){}了 ,起点和终点之间需要和subscribe来连接起来了。
那么中间第二行和第三行干什么呢? 第二行表示上面的是运行在IO线程的(网络请求),下面的是运行在主线程里的。基本使用就这么简单。当然 ,还有简化版的
api.getProject()
.subscribeOn(Schedulers.io()) // 上面 异步
.observeOn(AndroidSchedulers.mainThread()) // 下面 主线程
.subscribe(new Consumer<ProjectBean>() {
@Override
public void accept(ProjectBean projectBean) throws Exception {
}
});
当然还可以使用lambda表达式
api.getProject()
.subscribeOn(Schedulers.io()) // 上面 异步
.observeOn(AndroidSchedulers.mainThread()) // 下面 主线程
.subscribe(v->{
});
上面三种都是一样的结果的,需要注意的是 lambda表达是使用起来很简洁,但是可读性较差,根据需要使用。
- 再举个例子,比如说我们从网络上下载图片 转换成bitmap显示出来
// 起点
Observable.just(PATH)
.map(new Function<String, Bitmap>() {
@Override
public Bitmap apply(String s) throws Exception {
URL url = new URL(PATH);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(5000);
int responseCode = httpURLConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream inputStream = httpURLConnection.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
return bitmap;
}
return null;
}
})
//加个水印
.map(new Function<Bitmap, Bitmap>() {
@Override
public Bitmap apply(Bitmap bitmap) throws Exception {
Paint paint = new Paint();
paint.setTextSize(88);
paint.setColor(Color.RED);
return drawTextToBitmap(bitmap, "同学们大家好",paint, 88 , 88);
}
})
// 加个日志记录
.map(new Function<Bitmap, Bitmap>() {
@Override
public Bitmap apply(Bitmap bitmap) throws Exception {
Log.d(TAG, "apply: 是这个时候下载了图片啊:" + System.currentTimeMillis());
return bitmap;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
// 订阅 起点 和 终点 订阅起来
.subscribe(
// 终点
new Observer<Bitmap>() {
@Override
public void onSubscribe(Disposable d) {
progressDialog = new ProgressDialog(DownloadActivity.this);
progressDialog.setTitle("download run");
progressDialog.show();
}
@Override
public void onNext(Bitmap bitmap) {
image.setImageBitmap(bitmap);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
if (progressDialog != null)
progressDialog.dismiss();
}
});
可以看到 我们在起点和终点之间添加删除任何功能都很简单 ,只需要添加对应的map就行。这在传统思想的编程里是做不到的。
功能防抖
什么叫功能防抖呢?比如一个按钮,正常情况下,在一秒时间内我不停的点击 ,它就会响应N次,这就是抖动了 ,显然像这种不合理的用户操作我们应该屏蔽掉。这就需要用到RxBinding的功能了,代码如下:
RxView.clicks(btnView)
// 1秒钟之内 只会响应一次
.throttleFirst(1000,TimeUnit.MILLISECONDS)
.subscribe(new Consumer<Object>() {
@Override
public void accept(Object o) throws Exception {
}
});
网络嵌套
假设有这样的一个需求,点击按钮,查询主数据里的全部item的详细数据,那么,正常的思路就是 先查主数据,成功之后再根据每个Id 查询 item里的数据,这里就会有网络嵌套。之前代码如下:
RxView.clicks(btnView)
.throttleFirst(1000, TimeUnit.MILLISECONDS)
.subscribe(new Consumer<Object>() {
@Override
public void accept(Object o) throws Exception {
api.getProject() // 查询主数据
.compose(DownloadActivity.rxud())
.subscribe(new Consumer<ProjectBean>() {
@Override
public void accept(ProjectBean projectBean) throws Exception {
for (ProjectBean.DataBean dataBean : projectBean.getData()) { // 10
// 查询item数据
api.getProjectItem(1, dataBean.getId())
.compose(DownloadActivity.rxud())
.subscribe(new Consumer<ProjectItem>() {
@Override
public void accept(ProjectItem projectItem) throws Exception {
Log.d(TAG, "accept: " + projectItem); // 可以UI操作
}
});
}
}
});
}
});
代码很难看,如果再多来几层嵌套 维护成本就很大。Rx的思想是事件流的形式的 ,完全可以解决这个问题,看Rx解决的代码:
RxView.clicks(btnView)
.throttleFirst(1000,TimeUnit.MILLISECONDS)
.observeOn(Schedulers.io())
.flatMap(new Function<Object, ObservableSource<ProjectBean>>() {
@Override
public ObservableSource<ProjectBean> apply(Object o) throws Exception {
//请求主数据
return api.getProject();
}
})
.flatMap(new Function<ProjectBean, ObservableSource<ProjectBean.DataBean>>() {
@Override
public ObservableSource<ProjectBean.DataBean> apply(ProjectBean projectBean) throws Exception {
//自己定义一个发射器 可以发射N个 一个一个的发射,相当于 for循环
return Observable.fromIterable(projectBean.getData());
}
})
.flatMap(new Function<ProjectBean.DataBean, ObservableSource<ProjectItem>>() {
@Override
public ObservableSource<ProjectItem> apply(ProjectBean.DataBean dataBean) throws Exception {
//请求item的数据
return api.getProjectItem(1,dataBean.getId());
}
})
//切回主线程(更新UI)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<ProjectItem>() {
@Override
public void accept(ProjectItem projectItem) throws Exception {
}
});
可以看到代码优雅多了。