案例下载传送门
首先要了解什么是观察者
Android 开发中一个比较典型的例子是点击监听器 OnClickListener 。对设置 OnClickListener 来说, View 是被观察者, OnClickListener 是观察者,二者通过 setOnClickListener() 方法达成订阅关系。订阅之后用户点击按钮的瞬间,Android Framework 就会将点击事件发送给已经注册的 OnClickListener 。采取这样被动的观察方式,既省去了反复检索状态的资源消耗,也能够得到最高的反馈速度。
如图所示,通过 setOnClickListener() 方法,Button 持有 OnClickListener 的引用(这一过程没有在图上画出);当用户点击时,Button 自动调用 OnClickListener 的 onClick() 方法。另外,如果把这张图中的概念抽象出来(Button -> 被观察者、OnClickListener -> 观察者、setOnClickListener() -> 订阅,onClick() -> 事件),就由专用的观察者模式(例如只用于监听控件点击)转变成了通用的观察者模式。如下图:
在com.android.tools.build:gradle 3.0 以上版本依赖在gradle 中的声明写法
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
implementation 'io.reactivex.rxjava2:rxjava:2.x.y'
在com.android.tools.build:gradle 3.0 以下版本依赖在gradle 中的声明写法
compile 'io.reactivex.rxjava2:rxandroid:2.0.2'
compile 'io.reactivex.rxjava2:rxjava:2.x.y'
- 作用:决定什么时候出发时间以及出发怎样的事件
- 创建方法:
Observable.just (T t) //参数为单个的
Observable.flapMap() //参数为多个的
Observable.from (T [] t) //参数为数组
Observable.from (Iterable extends T>) //参数为Iterable (ArrayList的一种实现)
- 作用:当事件触发的时候将有怎样的行为
- 实现类:
Observer \ Subscriber
- 作用:使被观察者与观察者建立联系
-方法:
observable.subscribe(observer);
observable.subscribe(subscriber);
onNext():普通事件
onCompleted():时间队列完结
onError():时间队列异常
注意:onCompleted()与onError()是互斥的,调用了其中一个另一个就不会被触发
- 作用:控制线程,指定某一段代码在哪个线程执行
- 内置的Scheduler
Schedulers.newThread(); //总是启用新线程,并在新的线程中执行操作
Schedulers.io(); //I/O操作(读写文件、读写数据库、网络请求)所使用的线程。行为模式和newThread差不多,区别在于IO的内部实现的是一个无数量上线的线程池,可以重用空闲线程,因此多数情况下IO()比newThread()更有效率,不要把计算工作放在IO中,可以避免不必要的创建线程
Schedulers.computation(); //计算使用的Schedulers,这个计算指的是CPU的密集型计算,既不会被I/O 等操作限制性能操作,例如图形的计算,这个Scheduler使用的固定的线程池,大小为CPU的核数。不要把IO操作放在computation()中,否则IO操作等待时间会浪费CPU.
Schedulers.trampoline(); //调度器会将任务按添加顺序依次放入当前线程中等待执行(当前线程就是Schedulers.trampoline调度器创建Worker对象后,Worker对象调用scedule方法所在的线程)。线程一次只执行一个任务,其余任务排队等待,一个任务都执行完成后再开始下一个任务的执行。
Schedulers.single(); //拥有一个线程单例,所有的任务都在这一个线程中执行,当此线程中有任务执行时,其他任务将会按照先进先出的顺序依次执行。
Scheduler.from(@NonNull Executor executor); //指定一个线程调度器,由此调度器来控制任务的执行策略。
AndroidSchedulers.mainThread(); //在Android UI线程中执行任务,为Android开发定制。
Observable.subscribeOn(); //指subscribe() 所发生的线程,即Observable.onSubscribe被激活时所处的线程。或者叫做事件产生的线程。
//若多次设定,则只有一次起作用。
Observable.observeOn(); //指Subscriber所运行在的线程,或者叫做事件的消费线程。(可以理解为现在要去渲染页面了,需要到主线程中去)
//若多次设定,每次均起作用。
基本概念1-4应用
遍历数组,找出数组中b开头的元素,将其转换为大写
sample1:fori实现
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
String[] arr = {"shanghai", "beijing", "chengdu"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for (int i = 0; i < arr.length ; i++) {
if (arr[i].startsWith("b")){
arr[i] = arr[i].toUpperCase();
Log.i(TAG,arr[i]);
}
}
}
}
输出结果:06-30 10:48:02.487 747-747/com.kwunyamshan.rxjava I/MainActivity: BEIJING
sample2:RxJava实现
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
String[] arr = {"shanghai", "beijing", "chengdu"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//导包不要导错 import io.reactivex.Observable;
Observable observable = Observable.fromArray(arr);
//添加一个过滤器 , 把字母b开头的元素过滤出来
Observable filterObservable = observable.filter(new Predicate() {
@Override
public boolean test(String s) throws Exception {
return s.startsWith("b");
}
});
//接收结果
Observer observer = new Observer() {
@Override
public void onSubscribe(Disposable d) {}
//真正处理事件的方法
@Override
public void onNext(String s) {
s = s.toUpperCase();
Log.i(TAG, s);
}
@Override
public void onError(Throwable e) {}
@Override
public void onComplete() {}
};
//关联观察者与被观察者
filterObservable.subscribe(observer);
}
输出结果:06-30 10:49:00.371 3261-3261/com.kwunyamshan.rxjava I/MainActivity: BEIJING
sample3:RxJava实现(优化)
看起来sample1中的代码比sample2中的代码还要简洁,脾气暴躁的大哥就要来干我了,别急一步一步来你就明
白了,下面是对RXJava进行了优化
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
String[] arr = {"shanghai", "beijing", "chengdu"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Observable.fromArray(arr)
////filter过滤以后返回的类型也是Observable, 所以变量可以删去,链式编程
//Observable filterObservable = observable.filter(new Predicate() {
.filter(new Predicate() {
@Override
public boolean test(String s) throws Exception {
return s.startsWith("b");
}
})
//这里如果需要做一件事则不需要new Observer,
.subscribe(new Consumer() {
@Override
public void accept(String s) throws Exception {
s = s.toUpperCase();
Log.i(TAG, s);
}
});
}
输出结果:06-30 10:51:00.789 3261-3261/com.kwunyamshan.rxjava I/MainActivity: BEIJING
你会发现这样写逻辑条理会变得特别清楚(如果会用lambda表达式的话则会更加的简洁),一个方法做一件
事情,再加多少个方法逻辑也不会乱,这样写不管过了多久再回过头来看这段代码是不是也不迷糊了
基本概念1-5应用
获取图片,展示到ImageView上,出现异常时打印toast报错(涉及到切换线程)
sample1:不考虑线程问题,简单实现1对1数据转换(关键字just)
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
private ImageView ivAvatar;
private int imgId = R.mipmap.avatar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ivAvatar = findViewById(R.id.iv_avatar);
//当对象是单个的时候调用just
Observable.just(imgId)
//将图片id转化为drawable
.map(new Function() {
@Override
public Drawable apply(Integer integer) throws Exception {
return getResources().getDrawable(imgId);
}
})
//把drawable设置到ImageView上
.subscribe(new Consumer() {
@Override
public void accept(Drawable drawable) throws Exception {
ivAvatar.setImageDrawable(drawable);
}
});
}
sample2:增加线程切换,1对1数据转换(关键字just)不了解线程切换的翻回去看 5. Scheduler
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
private ImageView ivAvatar;
private int imgId = R.mipmap.avatar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ivAvatar = findViewById(R.id.iv_avatar);
//当对象是单个的时候调用just
Observable
.just(imgId)
//将图片id转化为drawable, 比方这个地方走网络请求图片的话这样肯定是走不成了
//所以我们需要在这里添加切换线程的方法
.subscribeOn(Schedulers.io())
//以下代码在子线程中执行
.map(new Function() {
@Override
public Drawable apply(Integer integer) throws Exception {
return getResources().getDrawable(imgId);
}
})
//把drawable设置到ImageView上 , 渲染页面的逻辑在主线程中调用
.observeOn(AndroidSchedulers.mainThread())
//以下代码在主线程中执行
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {}
@Override
public void onNext(Drawable drawable) {
ivAvatar.setImageDrawable(drawable);
}
@Override
public void onError(Throwable e) {
Toast.makeText(MainActivity.this,e.getMessage(),1).show();
}
@Override
public void onComplete() {}
});
}
基本概念1-5应用
现在有两张表,一张单位职工表,一张职工工资表,根据职工姓名(职工表)查询职工每个月的工资(工资表)
sample1:fori实现1对多查询
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SalaryBean jan = new SalaryBean("3000", 8);
SalaryBean feb = new SalaryBean("3400", 9);
SalaryBean mar = new SalaryBean("3000", 10);
SalaryBean apr = new SalaryBean("3400", 11);
SalaryBean may = new SalaryBean("5500", 12);
List salaryList = new ArrayList<>();
salaryList.add(jan);
salaryList.add(feb);
salaryList.add(mar);
salaryList.add(apr);
salaryList.add(may);
StaffBean 秃头李 = new StaffBean("李老板", salaryList);
for (int i = 0; i < 秃头李.getmSalaryList().size(); i++) {
SalaryBean salaryBean = 秃头李.getmSalaryList().get(i);
Log.i(TAG, "您" + salaryBean.getMonth() + "月份" + "的薪水:" + salaryBean.getSalary());
}
}
}
输出结果:
06-30 10:53:48.641 8985-8985/com.kwunyamshan.rxjava I/MainActivity: 您8月份的薪水:3000
06-30 10:53:48.641 8985-8985/com.kwunyamshan.rxjava I/MainActivity: 您9月份的薪水:3400
06-30 10:53:48.641 8985-8985/com.kwunyamshan.rxjava I/MainActivity: 您10月份的薪水:3000
06-30 10:53:48.641 8985-8985/com.kwunyamshan.rxjava I/MainActivity: 您11月份的薪水:3400
06-30 10:53:48.642 8985-8985/com.kwunyamshan.rxjava I/MainActivity: 您12月份的薪水:5500
sample2:用RxJava实现1对多查询(关键字flatMap)
public class MainActivity extends AppCompatActivity {
public static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SalaryBean jan = new SalaryBean("3000", 8);
SalaryBean feb = new SalaryBean("3400", 9);
SalaryBean mar = new SalaryBean("3000", 10);
SalaryBean apr = new SalaryBean("3400", 11);
SalaryBean may = new SalaryBean("5500", 12);
List salaryList = new ArrayList<>();
salaryList.add(jan);
salaryList.add(feb);
salaryList.add(mar);
salaryList.add(apr);
salaryList.add(may);
StaffBean 秃头李 = new StaffBean("李老板", salaryList);
//查询单个的员工用just
Observable
.just(秃头李)
//查询员工每个月工资情况用flatMap
.flatMap(new Function>() {
@Override
public Observable apply(StaffBean staffBean) throws Exception {
return Observable.fromIterable(staffBean.getmSalaryList());
}
})
.subscribe(new Consumer() {
@Override
public void accept(SalaryBean salaryBeans) throws Exception {
Log.i(TAG, "您" + salaryBeans.getMonth() + "月份" + "的薪水:" + salaryBeans.getSalary());
}
});
}
输出结果:
06-30 10:54:16.278 10183-10183/com.kwunyamshan.rxjava I/MainActivity: 您8月份的薪水:3000
06-30 10:54:16.278 10183-10183/com.kwunyamshan.rxjava I/MainActivity: 您9月份的薪水:3400
06-30 10:54:16.278 10183-10183/com.kwunyamshan.rxjava I/MainActivity: 您10月份的薪水:3000
06-30 10:54:16.278 10183-10183/com.kwunyamshan.rxjava I/MainActivity: 您11月份的薪水:3400
06-30 10:54:16.278 10183-10183/com.kwunyamshan.rxjava I/MainActivity: 您12月份的薪水:5500
案例下载传送门