Retrofit + RxJava + OkHttp 的简单封装
第一次写有点小激动 算是总结吧!
最近一直听群友说Retrofit + Rxjava + OkHttp有多牛逼多好用多强大....让我产生了很大兴趣....然后去网络上搜索了一翻....很多资料....但是对于基础不是很好的我....很不友好....(资质太低不要嘲笑我)....看完基本还是没明白....
首先推荐一篇看过觉得好的文章
给初学者的RxJava2.0教程 (作者Season_zlc)
阅读以下内容 请先把Retrofit + RxJava + OkHttp先了解一下 这样看下面的东西 才能对你理解起来有帮助
OkHttp
OkHttp是时下最火的HTTP请求框架,别问我为什么,Google都放弃了httpclient,用OkHttp来替代他,你还要问么? so....学习一个新的请求框架,适应形势,还是非常有必要的!
Retrofit
Retrofit + OkHttp我之前学习的时候,看别人的代码一直不知道Retrofit和OkHttp二者是有什么区别? 查阅资料后了解到,OkHttp与HTTPClient、HTTPConnection是一个级别的,是系统提供的较为原装的网络请求库。而Retrofit则是基于OkHttp的再次封装过,与Volley这种已经被封装过的网络库是一个级别的。其实就是套了一层把很多的坑都给你门填好了,而且最主要的是,做了很多安全上的改进,还有能更好的支持RxJava。
Rxjava
看完[Season_zlc]的讲解后,让我了解到了RxJava 是通过实现了线程调度器让开发者更方便且自由的调度线程,被观察者和订阅者的方式和强大的操作符,还有引以为傲的链式调用,满足者开发者各种需求。
那为什么要RxJava + Retrofit + OkHttp要一起使用呢?
其实原因很简单Retrofit用过的人都知道,Retrofit 和 OkHttp 2者都没有实现结果成功后在UI线程回调,而是在IO线程回调的。那你肯定是不能更新控件,然道你要打算去写handle来通知UI线程更新么,你不觉得很low么? 而且代码逻辑很乱么? 而使用Rxjava不仅让你的代码看起来更加优雅易懂,并且Retrofit本身也是支持RxJava的。个人想法,也许作者最初的目的本来就是为了和RxJava配套使用。
以下代码均取自Fazhi项目wanggang老师的教学代码 最后我会提供代码供大家下载 不用担心!
一、首先我门定义API类
public interface Iapi {
@POST("userLogin")
Observable<BaseResponse<UserBean>> userLogin(@Body RequestBody params);
}
二、初始化Retrofit + OkHttp的请求类 (已经按步骤在代码里注释好了)
这个类的代码很明显,我门先将繁琐的Retrofit和OkHttp初始化,然后通过retrofit.create(Iapi.class)拿到最先定义的IPI类的实例类对象,有了实例类对象后就可以将RequestBody请求体传入发起请求了。
public class NetRequest {
private static final String TAG = "===NET REQUEST===";
private static NetRequest instance;
private OkHttpClient client;
private Retrofit retrofit;
private Iapi iapi;
// 第一步 构造RequestBody请求体
public static RequestBody generateReqBody(HashMap<String, Object> map){
JSONObject params = new JSONObject();
params.putAll(map);
return RequestBody.create(NetConfig.TypeJSON, params.toJSONString());
}
// 第二步 构建单例模式
public static NetRequest getInstance() {
if (instance == null) {
synchronized (NetRequest.class) {
if (instance == null) {
instance = new NetRequest();
}
}
}
return instance;
}
// 第二步 1.获得单例模式时会初始化该类 初始化时就构造好OkHttp和Retrofit2个对象 并且设置好参数
public NetRequest() {
client = getClient();
retrofit = getRetrofit();
}
// 第二步 2.初始化OkHttp的参数 并拿到Client请求对象
private OkHttpClient getClient() {
// HttpLoggingInterceptor是Log拦截器
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Log.i(TAG, message);
}
});
// 设置Log拦截器的拦截等级
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
return new OkHttpClient.Builder()
.addInterceptor(interceptor)// 拦截器设置给它
.connectTimeout(20, TimeUnit.SECONDS)// 请求超时时间
.readTimeout(20, TimeUnit.SECONDS)// 读取超时时间
.writeTimeout(20, TimeUnit.SECONDS)// 写入超时时间
.build();
}
// 第二步 3.初始化Retrofit的参数 并将Client设置给它
private Retrofit getRetrofit() {
return new Retrofit.Builder()
.client(client)// 传入OKhttp的client对象
.addConverterFactory(GsonConverterFactory.create())// 添加gson解析的支持
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())// 添加支持observable类型
.baseUrl(NetConfig.BASE_URL)// 传入URL
.build();
}
// 第四步 通过retrofit创建Iapi的实现类对象(retrofit已经帮你实现好了 不需要自己去实现 只要调用方法)
// 待会model要用来调你写好的Iapi里的接口 接口里定义好了方法为userLogin(放第一步获取的请求体)
public Iapi getApi() {
if (iapi == null) {
iapi = retrofit.create(Iapi.class);
}
return iapi;
}
}
三、重写了Subscriber订阅者类
封装了一些每次回调可以不变的代码,并将onNext这个成功的回调丢给后面的继承者
另外:
1.该类是一个抽象类,继承Subscriber但并没有重写onNext()方法,因为后面要留给Presenter的实现类去回调成功后做各种操作,因此它并不属于重复而又不变的代码,所以在该类里并没有去实现它。
2.onError() 中我门做了统一的服务器错误代码处理,将错误代码区分开来,这里分二种情况:
- 请求服务器失败了,通常为网络连接失败404 500之类的
- 网络通畅请求成功了,但是给你返回了约定好的错误代码,通常1000 1001之类的,比如帐号不存在 未注册等等....
public abstract class NetSubscriber<T> extends Subscriber<T> {
private BaseView view;
@Override
public void onStart() {
super.onStart();
view = getView();
view.showLoading();
}
@Override
public void onCompleted() {
view.hideLoading();
}
@Override
public void onError(Throwable e) {
view.hideLoading();
ExceptionHandle.ResponeThrowable throwable;
//后台逻辑执行失败,抛出的异常
if(e instanceof ExceptionHandle.ResponeThrowable){
throwable = (ExceptionHandle.ResponeThrowable) e;
} else {
//网络操作执行的异常
throwable = ExceptionHandle.handleException(e);
}
view.showError(throwable.message);
}
public abstract BaseView getView();
}
四、封装RxJava的请求参数
1.Presenter中RxJava每次都要写初始化参数,比如设置请求时在IO线程,请求完后在UI线程。
2.设置拦截器好比Rxjava发了一个消息下来,中间被你拦截起来,按你设定的过滤规则决定让消息走哪边。
例如我门下面代码中,规则是:
如果服务器返回为true,我门放行让订阅者在onNext()接受到成功的json数据
如果服务器返回为false,我门让他走另一条路exception,让他进入统一错误代码处理中,然后回调订阅者的onErrer()提示用户,失败了的原因。
public class RxHelper {
// 设置请求时为IO线程,处理结果时在UI线程
public static Observable.Transformer schedulers() {
return new Observable.Transformer() {
@Override
public Object call(Object observable) {
return ((Observable)observable)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
};
}
// 本方法为设置拦截器
// 1.用了Map操作符 来拦截每一次的消息 过滤正确的放行给订阅者 失败的丢给错误码统一处理类
// 2.onErrorResumeNext设置如果是请求上的错误 比如网络错误/连接超时之类的 直接丢给错误码统一处理类
public static <T> Observable.Transformer transform() {
return new Observable.Transformer() {
@Override
public Object call(Object observable) {
return ((Observable)observable)
.map(new TransformerFun())// 设置拦截服务器连接成功后返回的内容 然后筛选
.onErrorResumeNext(new ServerExceptionFun());// 设置请求直接失败了的拦截
}
};
}
/**
* 请求失败 将错误代码的回调转接给统一处理类
* @param <T>
*/
static class ServerExceptionFun<T> implements Func1<Throwable,Observable<T>>{
@Override
public Observable<T> call(Throwable throwable) {
return Observable.error(ExceptionHandle.handleException(throwable));
}
}
/**
* 拦截器的具体逻辑
* @param <T>
*/
static class TransformerFun<T> implements Func1<BaseResponse<T>,T>{
@Override
public T call(BaseResponse<T> baseResponse) {
if(baseResponse.is_success()){
return baseResponse.getData();// 正确返回json
}
// 如果不正确 丢给错误代码统一处理类
ExceptionHandle.ServerException exception = new ExceptionHandle.ServerException();
exception.message = baseResponse.getError_content();
throw exception;
}
}
}
五、错误代码统一处理类
public class ExceptionHandle {
private static final int UNAUTHORIZED = 401;
private static final int FORBIDDEN = 403;
private static final int NOT_FOUND = 404;
private static final int REQUEST_TIMEOUT = 408;
private static final int INTERNAL_SERVER_ERROR = 500;
private static final int BAD_GATEWAY = 502;
private static final int SERVICE_UNAVAILABLE = 503;
private static final int GATEWAY_TIMEOUT = 504;
public static ResponeThrowable handleException(Throwable e) {
ResponeThrowable ex;
if (e instanceof HttpException) {
HttpException httpException = (HttpException) e;
ex = new ResponeThrowable(e, ERROR.HTTP_ERROR);
switch (httpException.code()) {
case UNAUTHORIZED:
case FORBIDDEN:
case NOT_FOUND:
case REQUEST_TIMEOUT:
case GATEWAY_TIMEOUT:
case INTERNAL_SERVER_ERROR:
case BAD_GATEWAY:
case SERVICE_UNAVAILABLE:
default:
ex.message = "网络错误";
break;
}
return ex;
} else if (e instanceof ServerException) {
ServerException resultException = (ServerException) e;
ex = new ResponeThrowable(resultException, resultException.code);
ex.message = resultException.message;
return ex;
} else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) {
ex = new ResponeThrowable(e, ERROR.PARSE_ERROR);
ex.message = "解析错误";
return ex;
} else if (e instanceof ConnectException) {
ex = new ResponeThrowable(e, ERROR.NETWORD_ERROR);
ex.message = "连接失败";
return ex;
} else if (e instanceof javax.net.ssl.SSLHandshakeException) {
ex = new ResponeThrowable(e, ERROR.SSL_ERROR);
ex.message = "证书验证失败";
return ex;
} else if (e instanceof ConnectTimeoutException) {
ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
ex.message = "连接超时";
return ex;
} else if (e instanceof java.net.SocketTimeoutException) {
ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
ex.message = "连接超时";
return ex;
} else {
ex = new ResponeThrowable(e, ERROR.UNKNOWN);
ex.message = "未知错误";
return ex;
}
}
/**
* 约定异常
*/
class ERROR {
/**
* 未知错误
*/
public static final int UNKNOWN = 1000;
/**
* 解析错误
*/
public static final int PARSE_ERROR = 1001;
/**
* 网络错误
*/
public static final int NETWORD_ERROR = 1002;
/**
* 协议出错
*/
public static final int HTTP_ERROR = 1003;
/**
* 证书出错
*/
public static final int SSL_ERROR = 1005;
/**
* 连接超时
*/
public static final int TIMEOUT_ERROR = 1006;
}
public static class ResponeThrowable extends Exception {
public int code;
public String message;
public ResponeThrowable(Throwable throwable, int code) {
super(throwable);
this.code = code;
}
}
public static class ServerException extends RuntimeException {
public int code;
public String message;
}
}
六、定义我门自己的BaseResponse成功响应的接收类
该类可有可无,主要用来替代Retrofit自带的ResponseBody,因为我门做了过滤,要用到is_success,当然如果你不做拦截也可以用他原来的ResponseBody。
public class BaseResponse<T> {
private boolean is_success;
private String error_content;
private T data;
public boolean is_success() {
return is_success;
}
public void setIs_success(boolean is_success) {
this.is_success = is_success;
}
public String getError_content() {
return error_content;
}
public void setError_content(String error_content) {
this.error_content = error_content;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
最后调用一下看看
HashMap<String, Object> map = new HashMap<>();
map.put("username", username);
map.put("password", passwrod);
RequestBody requestBody = NetRequest.generateReqBody(map);
Observable<BaseResponse<UserBean>> observable = NetRequest.getInstance().getApi() .userLogin(requestBody);
observable.compose(RxHelper.schedulers())
.compose(RxHelper.transform())
.subscribe(new NetSubscriber<UserBean>() {
@Override
public BaseView getView() { return view; }
@Override
public void onNext(UserBean userBean) {
// 成功的回调
}
});
对~老铁....就是这样....复制+粘贴....第一次....就这样没了
续 结合本封装的MVP项目架构 让你的Activity腰围瘦下来
FaZhi项目框架 Git下载 (向作者wanggang老师致敬)