目录
1、Observable和Observer默认在主线程中执行
3、subscribeOn() 指定发送事件的线程, observeOn() 指定接收事件的线程.
一、RxJava2线程调度
在 RxJava 的默认规则中,事件的发出和消费都是在同一个线程的。即上篇文章(RxJava2 使用详解之基础教程)事件的发出和消费都是在同一个线程。观察者模式本身的目的就是『后台处理,前台回调』的异步机制,因此异步对于 RxJava 是至关重要的。而要实现异步,则需要用到 RxJava 的另一个概念:
Scheduler
。
1、Observable和Observer默认在主线程中执行
话不多说上代码:(以下代码是测试Observable和Observer默认在主线程中执行)
Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
LogUtils.e(TAG, "Observable thread is : " + Thread.currentThread().getName());
LogUtils.e(TAG, "emitter=1");
emitter.onNext(1);
}
});
Observer<Integer> observer = new Observer<Integer>() {
@Override
public void onSubscribe(Disposable d) {
LogUtils.e(TAG, "onSubscribe");
}
@Override
public void onNext(Integer value) {
LogUtils.e(TAG, "Observer thread is :" + Thread.currentThread().getName());
LogUtils.e(TAG, "onNext: " + value);
}
@Override
public void onError(Throwable e) {
LogUtils.e(TAG, "onError");
}
@Override
public void onComplete() {
LogUtils.e(TAG, "onComplete");
}
};
observable.subscribe(observer);
运行结果:
从结果中可以看到,Observable和Observer都是在主线程中。这样肯定是不行的,因为我们在实际开发中,是不允许在主线程中进行耗时操作的,比如网络请求。我们肯定是要在子线程中网络请求,然后在主线程更新UI。那应该怎么做呢?
2、去子线程中发送事件,主线程接收事件
首先,我们需要先改变上游Observable发送事件的线程, 让它去子线程中发送事件, 然后再改变下游Observer的线程, 让它在主线程接收事件,然后做相应的处理. 通过RxJava内置的线程调度器就可以实现这个需求了。
代码如下:
Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
LogUtils.e(TAG, "Observable thread is : " + Thread.currentThread().getName());
LogUtils.e(TAG, "emitter=1");
emitter.onNext(1);
}
});
Observer<Integer> observer = new Observer<Integer>() {
@Override
public void onSubscribe(Disposable d) {
LogUtils.e(TAG, "onSubscribe");
}
@Override
public void onNext(Integer value) {
LogUtils.e(TAG, "Observer thread is :" + Thread.currentThread().getName());
LogUtils.e(TAG, "onNext: " + value);
}
@Override
public void onError(Throwable e) {
LogUtils.e(TAG, "onError");
}
@Override
public void onComplete() {
LogUtils.e(TAG, "onComplete");
}
};
observable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
运行结果:
可以看到,发送事件的线程改变了, 是在一个叫RxNewThreadScheduler-1的线程中发送的事件, 而下游在主线程中接收事件, 我们仅仅添加了下边两行代码就实现了这个需求。
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
3、subscribeOn() 指定发送事件的线程, observeOn() 指定接收事件的线程.
简单的来说, subscribeOn() 指定的是上游发送事件的线程, observeOn() 指定的是下游接收事件的线程.
多次指定上游的线程只有第一次指定的有效, 也就是说多次调用subscribeOn()只有第一次的有效。
多次指定下游的线程是可以的, 也就是说每调用一次observeOn() , 下游的线程就会切换一次。
我们实验一下,在上边代码上做如下修改:
observable.subscribeOn(Schedulers.newThread())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.subscribe(consumer);
这段代码中指定了两次上游发送事件的线程, 分别是newThread和IO线程, 下游也指定了两次线程,分别是main和IO线程.。
运行结果:
从上边的结果图中, 上游虽然指定了两次线程, 但只有第一次有效, 依然是在RxNewThreadScheduler-1 线程中, 而下游则在RxCachedThreadScheduler-2这个线程中, CacheThread其实就是IO线程池中的一个,其实下游是先切换到mainThread,之后切换到Schedulers.io,最后开始接受事件的。
我们再修改一下代码,在每次切换线程之后都打印一下当前线程,代码如下:
observable.subscribeOn(Schedulers.newThread())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.d(TAG, "After observeOn(mainThread), current thread is: " + Thread.currentThread().getName());
}
})
.observeOn(Schedulers.io())
.doOnNext(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.d(TAG, "After observeOn(io), current thread is : " + Thread.currentThread().getName());
}
})
.subscribe(consumer);
运行结果:
从结果中可以看到, 每调用一次observeOn() 线程便会切换一次, 因此如果我们要频繁切换线程的时候,就知道改怎么做了。
二、RxJava2的线程选择:
Schedulers.io() :代表io操作的线程, 通常用于网络,读写文件等io密集型的操作
Schedulers.computation() :代表CPU计算密集型的操作, 例如需要大量计算的操作
Schedulers.newThread() :代表一个常规的新线程
AndroidSchedulers.mainThread() :代表Android的主线程
RxJava内置的Scheduler足够满足我们平时开发的需求, RxJava内部使用的是线程池来维护这些线程, 效率也比较高.
三、实际开发中使用(网络请求)
Retrofit能够完美配合RxJava的方式来调用, 我们这里使用Retrofit2。
1、在app的build.gradle中添加如下依赖:(@DATE )
/*gson*/
implementation 'com.google.code.gson:gson:2.8.5'
/*导入Rxjava RxAndroid*/
implementation 'io.reactivex.rxjava2:rxjava:2.2.1'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
/*导入retrofit gson*/
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
/*转换器,请求结果转换成Model*/
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
/*配合Rxjava 使用*/
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
2、在manifest.xml中添加网络权限
<uses-permission android:name="android.permission.INTERNET"/>
3、建立Bean层 RecommendBannerInfo
测试网址:http://app.bilibili.com/x/banner?plat=4&build=411007&channel=bilih5
复制内容后通过gsonformat一键生成
public class RecommendBannerInfo {
private int code;
private List<DataBean> data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public List<DataBean> getData() {
return data;
}
public void setData(List<DataBean> data) {
this.data = data;
}
public static class DataBean {
/**
* title : 快来送上生日祝福吧!
* value : https://www.bilibili.com/blackboard/activity-AXhliINTe.html
* image : http://i0.hdslb.com/bfs/archive/06f2dde20b031270bbc0d74cced1eff17effbd8d.jpg
* type : 0
* weight : 1
* remark :
* hash : f45aae457de61bfb4c2ce7a0cd49e062
*/
private String title;
private String value;
private String image;
private int type;
private int weight;
private String remark;
private String hash;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getHash() {
return hash;
}
public void setHash(String hash) {
this.hash = hash;
}
}
}
4、定义ApiService 接口,这里的接口在网上找的bilibili的首页banner图接口:
public interface ApiService {
/**
* 首页推荐banner
*/
@GET("x/banner?plat=4&build=411007&channel=bilih5")
Observable<RecommendBannerInfo> getRecommendedBannerInfo();
}
5、配置我们的Retrofit请求工具类:
public class HttpMethods {
private static final String TAG = "HttpMethods";
public static final String BASE_URL = "http://app.bilibili.com/";
private static final long DEFAULT_TIMEOUT = 10_000L;
private Retrofit retrofit;
private OkHttpClient client;
public HttpMethods() {
client = new OkHttpClient.Builder()
// 添加通用的Header
/*.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request.Builder builder = chain.request().newBuilder();
builder.addHeader("token", "123");
return chain.proceed(builder.build());
}
})*/
/*
这里可以添加一个HttpLoggingInterceptor,因为Retrofit封装好了从Http请求到解析,
出了bug很难找出来问题,添加HttpLoggingInterceptor拦截器方便调试接口
*/
.addInterceptor(new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Log.d(TAG,"==============" + message);
}
}).setLevel(HttpLoggingInterceptor.Level.BODY))
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(new Gson()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
public <T> T createService(Class<T> clazz) {
return retrofit.create(clazz);
}
}
6、在Activity中调用请求如下:
/* 创建被观察者
* 和retorfit结合,请求网络数据
* */
HttpMethods httpMethods = new HttpMethods();
/* 创建观察者
* 创建一个Observer来处理Observable对象发出的字符串
* */
Observer<RecommendBannerInfo> observer = new Observer<RecommendBannerInfo>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(RecommendBannerInfo value) {
LogUtils.e(TAG, "onNext: 请求成功");
LogUtils.e(TAG, "onNext: "+value.getData().get(0).getTitle());
}
@Override
public void onError(Throwable e) {
LogUtils.e(TAG, "onError: 请求失败");
}
@Override
public void onComplete() {
LogUtils.e(TAG, "onComplete: 请求成功");
}
};
/*建立连接*/
httpMethods.createService(ApiService.class).getRecommendBannerInfo()
.subscribeOn(Schedulers.io()) //在IO线程进行网络请求
.observeOn(AndroidSchedulers.mainThread()) //在主线程处理请求结果
.subscribe(observer);
运行结果:
通过log日志看到,已经请求成功了。
如果在请求的过程中Activity已经被销毁了, 这个时候如果回到主线程去更新UI, 那么APP肯定就会崩溃。这种情况的话我们使用 Disposable 来处理。Disposable 调用dispose()方法时observer就不会再去接收事件。我们可以在Activity中将每次请求返回的这个Disposable 保存起来, 当Activity被销毁时, 关闭(就是解除订阅关系)即可。
首先在Activity中实例化
Disposable disposable;
之后在Observer中修改onSubscribe,如下
@Override
public void onSubscribe(Disposable d) {
disposable = d;
}
最后在Activity中添加onDestroy如下
@Override
protected void onDestroy() {
super.onDestroy();
mDisposable.dispose();//关闭
}
如果有多个Disposable的话就使用CompositeDisposable, RxJava中已经内置了一个容器CompositeDisposable, 每当我们得到一个Disposable时就调用CompositeDisposable.add()将它添加到容器中, 在Activity销毁的时候, 调用CompositeDisposable.clear() 关闭(就是解除订阅关系)即可,代码如下:
首先在Activity中实例化
CompositeDisposable compositeDisposable = new CompositeDisposable();
之后在Observer中修改onSubscribe,如下
@Override
public void onSubscribe(Disposable d) {
compositeDisposable.add(d); //添加到CompositeDisposable容器中
}
最后在Activity中添加onDestroy如下
@Override
protected void onDestroy() {
super.onDestroy();
compositeDisposable.clear(); //关闭
}