本文主要是讲RxJava的使用,没有分析其原理。
为什么学习RxJava?
改变思维来提升效率
RxJava是响应式编程的思想
响应式编程即一系列操作是连续的,是流水线,从起点流到终点不会断的,比如:请求下载图片 -> 获取返回的结果 -> 更新UI,这整个的请求和响应的代码可以写在一行代码中(即只有一个代表语句结束的分号“;”)。
响应式编程的思想最早是微软架构组提出的。
Rx思维: 响应式编程
生活中的例子:
起点(分发事件(PATH):我饿了)----------下楼-------去餐厅--------点餐----------> 终点(吃饭 消费事件)
程序中的例子:
起点(分发事件:点击登录)----------登录API-------请求服务器--------获取响应码----------> 终点(更新UI登录成功 消费事件)
学习经典架构
Github:https://github.com/ReactiveX/RxJava
Author:JakeWharton 被誉为Android之神
Star:42K
Rx系列
官网:http://reactivex.io/#
Rx即响应式编程,很多语言都有对应的Rx框架
RxJava操作符应用
RxJava的操作符非常多,不需要每一个都学,只需要学习大部分操作符的通用的内容,通过学习两个操作符即可。
学习资料
注意:RxJava入门与基础的内容 已梳理好了(此内容不讲)
入门相关的资料与博客:
https://www.jianshu.com/p/cd3557b1a474
https://www.cnblogs.com/lyysz/p/6344507.html
https://www.cnblogs.com/liushilin/p/7058302.html
https://www.jb51.net/article/92309.htm
https://zhuanlan.zhihu.com/p/31413825
基础相关的资料与博客:
https://github.com/ReactiveX/RxJava
/给学生的资料/RxJava全套使用完整版.md
/给学生的资料/RxJava操作符大全.xmind 同学们可以结合“RxJava操作符大全”
去网上查询具体操作符的使用(这是学习方法)
RxJava2.0 与 RxJava3.0 差异化:
https://blog.csdn.net/weixin_45258969/article/details/95386872
RxJava 整个课程的安排
注意:RxJava 整个课程的安排(说明难度曲线)第一节课
第一次课目录
- 核心思想
- RxJava配合Retrofit
- 防抖
- 网络嵌套
- doOnNext运用
第一节课难度曲线:
RxJava核心思想
整个事件的流向过程中,下一层的类型根据上一层的类型进行变化。比如上一层流出的是String,那么下一层的接收的类型也是String
RxJava的三要素:
- 起点
- 事件
- 终点,事件从起点流向终点
传统的思维方式下载图片
注意:传统方式 来完成 “下载图片功能“,每一位开发者的思路都不同,就是传统的编程思维
// TODO 传统方式
// 传统方式: 思维无法固定,不同的人有不同的编程思维 (后面接手你写的项目,看不懂)
// A程序员:35356453 自己的思维 不同 封装方法....
// B程序员:46576576 自己的思维 不同 全部写在一起
// C程序员:43643654 自己的思维 不同 new Thread
// D程序员:66545655 自己的思维 不同 使用 线程池
// ...
// 零零散散,麻烦, 每个写代码的人的思维都不一样
public void downloadImageAction(View view) {
progressDialog = new ProgressDialog(this);
progressDialog.setTitle("下载图片中...");
progressDialog.show();
new Thread(new Runnable() {
@Override
public void run() {
try {
URL url = new URL(PATH);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(5000);
int responseCode = httpURLConnection.getResponseCode(); // 才开始 request
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream inputStream = httpURLConnection.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
Message message = handler.obtainMessage();
message.obj = bitmap;
handler.sendMessage(message);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
RxJava的思维方式下载图片
map示意图:
/**
* RxJava思想思维编程:事件从起点流到终点,起点是被观察者,终点是观察者
* 中间可以通过map操作符插入一些功能
*
* @param view
*/
public void rxJavaDownloadImageAction(View view) {
// 起点
Observable.just(PATH) // 内部会分发 PATH Stirng // TODO 第二步
.map(new Function<String, Bitmap>() { // TODO 第三步
@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(); // 才开始 request
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()) // 给下面代码分配主线程;
*/
// .compose(rxud()) 对上面的
// .subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// 进行封装
.compose(rxud())
// 订阅:将 起点 和 终点 订阅起来
.subscribe(
// 终点
new Observer<Bitmap>() {
// 订阅开始
@Override
public void onSubscribe(Disposable d) {
// 预备 开始 要分发
// TODO 第一步
progressDialog = new ProgressDialog(DownloadActivity.this);
progressDialog.setTitle("download run");
progressDialog.show();
}
// TODO 第四步
// 拿到事件
@Override
public void onNext(Bitmap bitmap) {
image.setImageBitmap(bitmap);
}
// 错误事件
@Override
public void onError(Throwable e) {
}
// TODO 第五步
// 完成事件
@Override
public void onComplete() {
if (progressDialog != null)
progressDialog.dismiss();
}
}
);
}
RxJava配合Retrofit
Retrofit是对请求进行封装,然后交给okhttp执行真正的网络请求,再将响应结果交给RxJava。
- Retrofit处理请求出去的数据
- okhttp执行真正的网路请求
- RxJava处理响应回来的数据
public interface WangAndroidApi {
// 总数据
@GET("project/tree/json")
Observable<ProjectBean> getProject(); // 异步线程 耗时操作, Observable是起点,ProjectBean是事件
// Item数据
@GET("project/list/{pageIndex}/json") // ?cid=294
Observable<ProjectItem> getProjectItem(@Path("pageIndex") int pageIndex, @Query("cid") int cid); // 异步线程 耗时操作
}
private WangAndroidApi api;
/**
* TODO Retrofit+RxJava 查询 项目分类 (总数据查询)
*
* @param view
*/
public void getProjectAction(View view) {
// 获取网络API
api.getProject()
.subscribeOn(Schedulers.io()) // 上面 异步
.observeOn(AndroidSchedulers.mainThread()) // 下面 主线程
.subscribe(new Consumer<ProjectBean>() {
@Override
public void accept(ProjectBean projectBean) throws Exception {
Log.d(TAG, "accept: " + projectBean); // UI 可以做事情
}
});
}
View防抖
同一个View点了5次,只会执行1次操作。
网络嵌套
如何避免网络嵌套?如何做到不嵌套也能查询出这两层数据?使用flatMap操作符。
flatMap示意图:
flatMap的作用:可以对输入的数据产生多个输出。
起点可以分发一个数据,通过flatMap 产生多个输出。
onNext(1);
|
|
|
flatMap :自己分发 10个数据 给下面
1 --> 多发送 10次 1+“DDD”
|
|
|
subscribe{
1+“DDD”
1+“DDD”
1+“DDD”
1+“DDD”
1+“DDD”
1+“DDD”
1+“DDD”
1+“DDD”
1+“DDD”
1+“DDD”
}
/**
* RxJava
* RxJs
* Rxxxxx
* RxBinding 防抖
* <p>
* TODO 功能防抖 + 网络嵌套(这种是负面教程,嵌套的太厉害了)
* 2层嵌套
* 6层
*/
@SuppressLint("CheckResult")
private void antiShakeActon() {
// 注意:(项目分类)查询的id,通过此id再去查询(项目列表数据)
// 对那个控件防抖动?
Button btn_anti_shake = findViewById(R.id.bt_anti_shake);
RxView.clicks(btn_anti_shake)
.throttleFirst(2000, TimeUnit.MILLISECONDS) // 2秒钟之内 响应你一次
.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 {
//通过遍历主数据查询item数据
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操作
}
});
}
}
});
}
});
}
/**
* TODO 功能防抖 + 避免网络嵌套 (解决嵌套的问题):使用 flatMap 操作符
*/
@SuppressLint("CheckResult")
private void antiShakeActonUpdate() {
// 注意:项目分类查询的id,通过此id再去查询(项目列表数据)
// 对那个控件防抖动?
Button btn_anti_shake = findViewById(R.id.bt_anti_shake);
RxView.clicks(btn_anti_shake)
.throttleFirst(2000, TimeUnit.MILLISECONDS) // 2秒钟之内 响应你一次
// 我只给下面切换异步线程, 上面的throttleFirst是运行在主线程
.observeOn(Schedulers.io())
.flatMap(new Function<Object, ObservableSource<ProjectBean>>() {//是通过ObservableSource实现多个输出的
@Override
public ObservableSource<ProjectBean> apply(Object o) throws Exception {
return api.getProject(); // 主数据
}
})
// 第一步不能使用map,因为 api 返回的是 Observbale<ProjectBean>,可以尝试下是否可以
/*
.map(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 {
//Observable.fromIterable会遍历projectBean.getData()的数据,有几个数据就发几次
return Observable.fromIterable(projectBean.getData()); // 我自己创建一个发射器 发多次
}
})
.flatMap(new Function<ProjectBean.DataBean, ObservableSource<ProjectItem>>() {
@Override
public ObservableSource<ProjectItem> apply(ProjectBean.DataBean dataBean) throws Exception {
return api.getProjectItem(1, dataBean.getId());
}
})
.observeOn(AndroidSchedulers.mainThread()) // 给下面切换到主线程
.subscribe(new Consumer<ProjectItem>() {
@Override
public void accept(ProjectItem projectItem) throws Exception {
// 如果我要更新UI
Log.d(TAG, "accept: " + projectItem);
}
});
}
doOnNext运用
使用场景:一个需求涉及到很多个接口,一个接口返回结果后才能进行下一个接口对应的操作。
doOnNext运用 (一行代码完成此需求)
RxJava的内存泄漏
@Override
protected void onDestroy() {
super.onDestroy();
// 必须这样写,最起码的标准
if (disposable != null)
if (!disposable.isDisposed())
disposable.dispose();
}
要在activity的onDestroy()中进行dispose()。
原因:
如果网络请求过程中比较慢,此时用户退出了页面,那么要进行dispose(),因为RxJava在分发事件的时候会根据isDisposed判断是否要中断分发,如果不进行dispose(),那么退出页面后RxJava会继续分发事件流,会继续执行后续的操作,而此时已经退出页面了,后续操作是不需要执行的,因此退出页面时需要调用dispose()。
作业
说说RxJava的核心思想的自己的理解?
有一个起点和一个终点,起点开始流向我们的“事件”,把事件流向到终点,只不过在流向的过程中,可以增加拦截,拦截时可以对“事件进行改变”,终点只关心它上一个拦截,与其他的无关。
JAVA设计模式之观察者模式
1、初步认识
观察者模式的定义:
在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。
大白话:
其实就是发布订阅模式,发布者发布信息,订阅者获取信息,订阅了就能收到信息,没订阅就收不到信息。
2、这个模式的结构图
3、可以看到,该模式包含四个角色
- 抽象被观察者角色:也就是一个抽象主题,它把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
- 抽象观察者角色:为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
- 具体被观察者角色:也就是一个具体的主题,在集体主题的内部状态改变时,所有登记过的观察者发出通知。
- 具体观察者角色:实现抽象观察者角色所需要的更新接口,一边使本身的状态与制图的状态相协调。
4、使用场景例子
有一个微信公众号服务,不定时发布一些消息,关注公众号就可以收到推送消息,取消关注就收不到推送消息。