我们都是知道Android开发必不可少的网络请求框架这几年经历了几次变更 android-async-http---->Volley、XUtils---->OkHttp---->Retrofit,这两年RxJava的流行让Retrofit着实火了一把,身为合格的Android开发人员要是对它不了解还真有点说不过去。
开篇
我是在去年的时候开始接触RxJava+Retrofit的,那时候还是RxJava1.x的版本,当时根据需求自己简单封装了一个网络库RxHttpUtils,因为只是针对公司项目的需求做的封装,所以有些局限性。可拓展性也不是很好(但是针对公司项目已经够用了),就这样那次封装之后就没怎么修改过相关代码了。
近期正好又开了新项目,网络请求这块后台有些变化,之前的网络框架不能满足了,加上RxJava2都已经出来了,所以就有了基于RxJava2重新封装的想法,这几天对RxHttpUtils1.x重构了一下,需要了解怎么使用的可以直接看RxHttpUtils 2.x里边有详细的使用说明,而且我也建议先看看使用说明加上demo自己试试看看,在我看来想要了解一件事必须先知道怎么去使用它,之后才是深入了解,解剖实现原理。
介绍
原始RxJava+Retrofit的请求
正式开始之前我们先看看原始的Retrofit是怎么使用
//先要构建出okHttpClient
OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();
okHttpBuilder.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10,TimeUnit.SECONDS)
.connectTimeout(10,TimeUnit.SECONDS)
.........省略n多配置
OkHttpClient okHttpClient = okHttpBuilder.build();
//构建出Retrofit实例
Retrofit.Builder builder = new Retrofit.Builder();
Retrofit retrofit = builder.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("your_upload_url")
.client(okHttpClient)
.build();
retrofit
.create(ApiService.class)
.getTop250(5)
.subscribeOn(Schedulers.io())//请求在IO线程中
.observeOn(AndroidSchedulers.mainThread())//回调在主线线程
.subscribe(new Observer<Top250Bean>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull Top250Bean top250Bean) {
//请求成功
}
@Override
public void onError(@NonNull Throwable e) {
//请求失败
}
@Override
public void onComplete() {
}
});
以上就是原始RxJava+Retrofit的请求方式,需要先构建出OKHttpClient,在实例化Retrofit最后才是create并且subscribe,中间还有线程切换的配置,大眼一看一个简单的请求写了这么多代码,这么麻烦,怎么一点也发现不了它的好处呐。别着急,好的东西只要亲身体验过后才知道。
我们总不能每次请求都写这么多代码吧,而且还都是重复的代码,既然是重复的代码我们就能把他抽离出去,进行简单的封装,使用的时候减少不必要的代码量。下边开始介绍如何封装。
封装
步骤 1、封装OkHttpClient 2、封装Retrofit 3、对线程切换的封装 4、对Observer封装
1、首先是要构建一个OkHttpClient,我们就新建一个类单独出来它
public class HttpClient {
private static HttpClient instance;
private OkHttpClient.Builder builder;
public HttpClient() {
builder = new OkHttpClient.Builder();
}
public static HttpClient getInstance() {
if (instance == null) {
synchronized (HttpClient.class) {
if (instance == null) {
instance = new HttpClient();
}
}
}
return instance;
}
public OkHttpClient.Builder getBuilder() {
return builder;
}
}
以上代码简单到根本需要注释,只是实例化出来okhttpclient,使用builder的方式是为了后边使用的时候方便定制自己的配置。
2、构建出Retrofit,我们也单独新建一个文件处理(代码结果和HttpClient 如出一辙)
public class RetrofitClient {
private static RetrofitClient instance;
private Retrofit.Builder mRetrofitBuilder;
private OkHttpClient.Builder mOkHttpBuilder;
public RetrofitClient() {
mOkHttpBuilder = HttpClient.getInstance().getBuilder();
mRetrofitBuilder = new Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(mOkHttpBuilder.build());
}
public static RetrofitClient getInstance() {
if (instance == null) {
synchronized (RetrofitClient.class) {
if (instance == null) {
instance = new RetrofitClient();
}
}
}
return instance;
}
public Retrofit.Builder getRetrofitBuilder() {
return mRetrofitBuilder;
}
public Retrofit getRetrofit() {
return mRetrofitBuilder.client(mOkHttpBuilder.build()).build();
}
}
3、对线程切换的封装
通过对最原始请求的分析可以看到每次都要对线程切换进行配置,是不是可以把它在进行封装呐,答案肯定是能,这里就要用到RxJava操作符的相关知识了,不了解的可以去搜一下哦,毕竟这样的文章已经有一把大了。
这里我们使用的是compose操作符
public class Transformer {
/**
* @param <T> 泛型
* @return 返回Observable
*/
public static <T> ObservableTransformer<T, T> switchSchedulers() {
return new ObservableTransformer<T, T>() {
@Override
public ObservableSource<T> apply(@NonNull Observable<T> upstream) {
return upstream
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.doOnSubscribe(new Consumer<Disposable>() {
@Override
public void accept(@NonNull Disposable disposable) throws Exception {
}
})
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread());
}
};
}
}
//封装之后具体是这样的使用的
retrofit
.......省略部分代码
.compose(Transformer.<Top250Bean>switchSchedulers())
4、对Observer的封装
//原始请求Observer的结果是这样的
new Observer<Top250Bean>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull Top250Bean top250Bean) {
//请求成功
}
@Override
public void onError(@NonNull Throwable e) {
//请求失败
}
@Override
public void onComplete() {
}
}
接下来我们对其进行封装,建一个BaseObserver类,继承Observer和ISubscriber,其中ISubscriber是我们提供的一个接口
public interface ISubscriber<T extends BaseResponse> {
void doOnSubscribe(Disposable d);
void doOnError(String errorMsg);
void doOnNext(T t);
void doOnCompleted();
}
其中我们定义了一个泛型T继承BaseResponse,也许这里你会有疑问,问什么要继承BaseResponse,因为部分请求结果格式都是以下格式
code为错误状态码 ; msg为错误描述信息
{
code: 0/400/401...,
msg: 错误描述...,
...
}
这样的格式便于我们对数据统一处理,比如后台规定code=200是请求成功并返回数据,否则就返回msg显示相关错误信息,code=400强制下线,等等自己的一套code规定,遇到这样的我们可以这样在底层统一处理
@Override
public void onNext(@NonNull T t) {
if(t.getCode==200){
doOnNext(t);
}else if (t.getCode==400){
//处理单设备登陆下线的逻辑
}else{
showToast(t.getMsg());
}
}
来看下完整的BaseObserver代码
public abstract class BaseObserver<T extends BaseResponse> implements Observer<T>, ISubscriber<T> {
private Toast mToast;
protected void doOnNetError() {
}
@Override
public void onSubscribe(@NonNull Disposable d) {
doOnSubscribe(d);
}
@Override
public void onNext(@NonNull T t) {
//这部分因为不同的后台处理逻辑不一样就没直接处理
//使用者可以根据自己的需求进行定制即可
doOnNext(t);
}
@Override
public void onError(@NonNull Throwable e) {
if (e instanceof SocketTimeoutException) {
setError(ApiException.errorMsg_SocketTimeoutException);
} else if (e instanceof ConnectException) {
setError(ApiException.errorMsg_ConnectException);
} else if (e instanceof UnknownHostException) {
setError(ApiException.errorMsg_UnknownHostException);
} else {
String error = e.getMessage();
showToast(error);
doOnError(error);
}
}
@Override
public void onComplete() {
doOnCompleted();
}
private void setError(String errorMsg) {
showToast(errorMsg);
doOnError(errorMsg);
doOnNetError();
}
/**
* Toast提示
*
* @param msg 提示内容
*/
protected void showToast(String msg) {
if (mToast == null) {
mToast = Toast.makeText(BaseRxHttpApplication.getContext(), msg, Toast.LENGTH_SHORT);
} else {
mToast.setText(msg);
}
mToast.show();
}
}
如果你不想这个方法返回时候那么多方法,就可以在继承BaseObserver写个CommonObserver做二次处理即可,github代码中已经封装了一个,有需求的可以看下,至此简单的模块封装已经完成了,各个类写好之后需要一个入口去提供方法供外部调用,我们建一个RxHttpUtils类,里边会暴露很多方法,去设置不同参数,前边我们封装的RetrofitClient和HttpClient我们都可以在拿到builder之后进行参数处理了,因为都是些对方法的封装,就不在此贴代码了,需要的可以去看看RxHttpUtils 2.x。
5、由于篇幅有限在此对文件下载的进度回调的封装就不做过多说明了,想了解的移步至源码去看看哈
最后来看看我们封装之后的效果吧
RxHttpUtils
.createApi(ApiService.class)
.getBook()
.compose(Transformer.<BookBean>switchSchedulers(loading_dialog))
.subscribe(new CommonObserver<BookBean>(loading_dialog) {
@Override
protected void getDisposable(Disposable d) {
//用于在onDestroy的时候取消订阅使用
}
@Override
protected void onError(String errorMsg) {
}
@Override
protected void onSuccess(BookBean bookBean) {
}
});
是不是代码减少很多,看起来更加清爽了
解答疑惑
相信到这里部分人还会有疑惑,为什么不把ApiService和compose也封装进去呐,毕竟网速也有那样去做的,我这里不想对使用者做太多干涉,让使用者自己去创建自己的ApiService,里边的方法名之类的都可以根据自己公司的命名规范去处理,另外使用compose操作符我们可以传一些参数请求的时候显示loading之类的,我代码中已经处理了