doOnNext 的运用
本文概述
- 本文以具体案例(需要频繁在异步线程与主线程之间切换),引出了doOnNext;记录了编写思路、编写细节、完整代码等;向读者展示了,doOnNext的独特魅力;
业务需求
-
需要频繁的(主/异步)切换线程
-
示意图:
应用场景:
- 例如银行(会频繁切换APP)
工程结构:
整体思路:
* Retrofit + RxJava
* 需求:
* 1.请求服务器注册操作
* 2.注册完成之后,更新注册UI
* 3.马上去登录服务器操作
* 4.登录完成之后,更新登录的UI
具体结构:
* wy.RxJava配合Retrofit。
* RxJava + Retrofit (请求网络OkHttp ---- Retorfit --- Observable)
*
* 1.OkHttp 请求网络 (Retorfit)
* 2.Retorfit 返回一个结果 (Retorfit) --- Observable
* 3.最终的结果 是RxJava中的 被观察者 上游 Observable
* 4.一行代码写完需求流程: 从上往下
* 1.请求服务器,执行注册操作(耗时)切换异步线程
* 2.更新注册后的所有 注册相关UI - main 切换主线程
* 3.请求服务器,执行登录操作(耗时)切换异步线程
* 4.更新登录后的所有 登录相关UI - main 切换主线程
*
* 5.看RxJava另外一种的执行流程
* 初始点 开始点 订阅
* 1.onSubscribe
* 2.registerAction(new RegisterRequest())
* 3..doOnNext 更新注册后的 所有UI
* 4.flatMap执行登录的耗时操作
* 5.订阅的观察者 下游 onNext 方法,更新所有登录后的UI
* 6.progressDialog.dismiss()
环境准备:
-
封装操作:为调用处上面分配异步线程,为调用处下面分配主线程
//DownLoadActivity.rxud public final static <UD> ObservableTransformer<UD, UD> rxud() { return new ObservableTransformer<UD, UD>() { @Override public ObservableSource<UD> apply(Observable<UD> upstream) { return upstream.subscribeOn(Schedulers.io()) // 给上面代码分配异步线程 .observeOn(AndroidSchedulers.mainThread()) // 给下面代码分配主线程; .map(new Function<UD, UD>() { @Override public UD apply(UD ud) throws Exception { Log.d(TAG, "apply: 我监听到你了,居然再执行"); return ud; } }); // ..... ; } }; }
-
封装操作:封装了Retrofit(返回一个Retrofit 实例)
package com.xiangxue.rxjavademo.doOnNext.retrofit_okhttp_rxjava.retrofit_okhttp; import java.util.concurrent.TimeUnit; import butterknife.BuildConfig; import okhttp3.OkHttpClient; import okhttp3.logging.HttpLoggingInterceptor; import retrofit2.Retrofit; import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; public class MyRetrofit { // 把Retrofit给Build出来 Retrofit给创建出来 public static Retrofit createRetrofit() { OkHttpClient.Builder builder = new OkHttpClient.Builder(); builder.readTimeout(10, TimeUnit.SECONDS); builder.connectTimeout(9, TimeUnit.SECONDS); if (BuildConfig.DEBUG) { HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); builder.addInterceptor(interceptor); } return new Retrofit.Builder().baseUrl("http://xxxxxxx") .client(builder.build()) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); } }
业务代码:分开写
// TODO 方式一 分开写
@SuppressLint("CheckResult")
public void request(View view) {
// 1.请求服务器注册操作
// 2.注册完成之后,更新注册UI
MyRetrofit.createRetrofit().create(IReqeustNetwork.class)
//注册操作:异步操作,耗时
.registerAction(new RegisterRequest())
//为上面分配异步线程,为下面分配主线程
.compose(DownloadActivity.rxud())
//RxJava 处理终点信息
.subscribe(new Consumer<RegisterResponse>() {
@Override
public void accept(RegisterResponse registerResponse) throws Exception {
// 更新注册相关的所有UI
// .....
}
});
// 3.马上去登录服务器操作
// 4.登录完成之后,更新登录的UI
MyRetrofit.createRetrofit().create(IReqeustNetwork.class)
//登录操作:异步操作,耗时
.loginAction(new LoginReqeust())
//为上面分配异步线程,为下面分配主线程
.compose(DownloadActivity.rxud())
//RxJava 处理终点信息
.subscribe(new Consumer<LoginResponse>() {
@Override
public void accept(LoginResponse loginResponse) throws Exception {
// 更新登录相关的所有UI
// .....
}
});
}
业务代码:将整体业务逻辑合并
-
思路:
/** * 一行代码 实现需求 * 需求: * 还有弹出加载 * * 1.请求服务器注册操作 * * 2.注册完成之后,更新注册UI * * 3.马上去登录服务器操作 * * 4.登录完成之后,更新登录的UI */
-
完整代码:
private ProgressDialog progressDialog; Disposable disposable; public void request2(View view) { MyRetrofit.createRetrofit().create(IReqeustNetwork.class) // todo 1.请求服务器注册操作(应当在异步线程) .registerAction(new RegisterRequest()) //给上面分配异步线程 .subscribeOn(Schedulers.io()) // 给下面分配主线程 .observeOn(AndroidSchedulers.mainThread()) //编写细节一:这里是不能写.subScribe(new Comsumer) .doOnNext(new Consumer<RegisterResponse>() { // todo 3 @Override public void accept(RegisterResponse registerResponse) throws Exception { // todo 2.注册完成之后,更新注册UI } }) // todo 3.马上去登录服务器操作(切换到异步线程) // 给下面分配了异步线程 .observeOn(Schedulers.io()) //使用flatMap解决嵌套导致代码结构不美观 //编写细节二:flatMap的第二个参数应该写什么? .flatMap(new Function<RegisterResponse, ObservableSource<LoginResponse>>() { @Override public ObservableSource<LoginResponse> apply(RegisterResponse registerResponse) throws Exception { Observable<LoginResponse> loginResponseObservable = MyRetrofit.createRetrofit().create(IReqeustNetwork.class) .loginAction(new LoginReqeust()); //返回的是登录响应的结果 return loginResponseObservable; } }) // 给下面 执行主线程:因为后面第四步的时候要更新UI .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<LoginResponse>() { // 一定是主线程,为什么,因为 subscribe 马上调用onSubscribe @Override public void onSubscribe(Disposable d) { // TODO 1 progressDialog = new ProgressDialog(RequestActivity.this); progressDialog.show(); // UI 操作 disposable = d; } @Override public void onNext(LoginResponse loginResponse) { // todo 5 // TODO 4.登录完成之后,更新登录的UI } @Override public void onError(Throwable e) { } // todo 6 @Override public void onComplete() { // 杀青了 if (progressDialog != null) { progressDialog.dismiss(); } } }); }
编写细节:
-
编写细节一:在第一次切换到main之后是不能写subScribe的,要写doOnNext
-
写subScribe:返回Disposable(此时流程会终止掉了)
-
写doOnNext:返回Observable
-
此时流程不会终止,整体业务上是不断添加卡片进行事件的拦截(进行操作)而不是将流程给断掉了
-
-
-
编写细节二:flatMap的第二个参数应该写什么?
-
flatMap的第一个参数:此卡片接收的事件类型
- 注册响应
-
flatMap的第一个参数:此卡片向后分发的事件类型
- 登录响应
-
-
编写细节三:线程的切换
-
给上面分配异步线程
.subscribeOn(Schedulers.io())
-
给下面分配主线程
.observeOn(AndroidSchedulers.mainThread())
-
给下面分配异步线程
.observeOn(Schedulers.io())
-
-
实际的业务走向流程
-
第一步:弹出加载框
progressDialog = new ProgressDialog(RequestActivity.this); progressDialog.show();
-
第二步:请求服务器进行注册操作
.registerAction(new RegisterRequest())
-
第三步:更新注册后的UI
.doOnNext(new Consumer<RegisterResponse>() { // todo 3 @Override public void accept(RegisterResponse registerResponse) throws Exception { // todo 2.注册完成之后,更新注册UI } })
-
第四步:请求服务器进行登录操作
.flatMap(new Function<RegisterResponse, ObservableSource<LoginResponse>>() { @Override public ObservableSource<LoginResponse> apply(RegisterResponse registerResponse) throws Exception { Observable<LoginResponse> loginResponseObservable = MyRetrofit.createRetrofit().create(IReqeustNetwork.class) .loginAction(new LoginReqeust()); return loginResponseObservable; } })
-
第五步:更新登录UI
@Override public void onNext(LoginResponse loginResponse) { // todo 5 // TODO 4.登录完成之后,更新登录的UI }
-
第六步:整个链条结束
// todo 6 @Override public void onComplete() { // 杀青了 if (progressDialog != null) { progressDialog.dismiss(); } }
-
-
细节四:RxJava起点到终点是单向的,从源码中来的
-
细节五:Disposable内存泄漏问题
- 基础:Rx 在每次事件分发前内部会判断Disposable 是否被中断,没有被中断才分发,中断了就不会分发;
- 具体场景:在Activity最网络请求,但网卡,但是这个时候用户杀掉了Activity;此时,在事件分发的时候就需要去检测中断标记,此时终止事件流动(避免了内存泄漏)
//声明 Disposable Disposable disposable; //在订阅处给 Disposable赋值 .subscribe(new Observer<LoginResponse>() { // 一定是主线程,为什么,因为 subscribe 马上调用onSubscribe @Override public void onSubscribe(Disposable d) { // TODO 第一步:订阅后弹出加载框 progressDialog = new ProgressDialog(RequestActivity.this); progressDialog.show(); // UI 操作 disposable = d; } //在Activity回收的时候,就要把这个 Disposable回收掉,不然有可能出现内存泄漏 @Override protected void onDestroy() { super.onDestroy(); // 必须这样写,最起码的标准 if (disposable != null) if (!disposable.isDisposed()) disposable.dispose(); }
-
细节六:onSubscribe一定执行在主线程
-
因为请求函数是由UI控件触发---》request是主线程
-
订阅(subscribe)也是主线程,一订阅后会立马触发onSubscribe
-
因此,onSubscribe也是主线程
-
这个里面可以写UI操作
-
-