每一个App都避免不了要进行网络请求,从最开始的用谷歌封装的volley到再到android-async-http再到OKHttpUtils再到如今的Retrofit和RxJava,从我本身用后的体验来看,用了retrofit和RxJava真的回不去了,回不去了,不去了,去了,了…(哈哈,原本还想分析下这四个的区别,网上这样的文章不少,我就不必多添乱了-.-)。很少逼逼,下面开始正文。java
compile 'io.reactivex:rxandroid:1.2.0' //Rxjava专门针对anroid封装的RxAndroid
compile 'io.reactivex:rxjava:1.1.5'
compile 'com.squareup.retrofit2:retrofit:2.0.2' //retrofit
compile 'com.squareup.retrofit2:converter-gson:2.0.2' //gson converter
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2' //Retrofit专门为Rxjava封装的适配器
compile 'com.google.code.gson:gson:2.6.2' //Gson
compile 'com.squareup.okhttp3:logging-interceptor:3.1.2' //打印网络请求的log日志
创建一个工厂类react
public class ServiceFactory {
private final Gson mGsonDateFormat;
public ServiceFactory(){
mGsonDateFormat = new GsonBuilder()
.setDateFormat("yyyy-MM-dd hh:mm:ss")
.create();
}
private static class SingletonHolder{
private static final ServiceFactory INSTANCE = new ServiceFactory();
}
public static ServiceFactory getInstance(){
return SingletonHolder.INSTANCE;
}
public <S> S createService(Class<S> serviceClass){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constant.BASE_URL) //Retrofit2 base url 必须是这种格式的:http://xxx.xxx/
.client(getOkHttpClient())
--------------------------添加Gson工厂变换器也就是不用管数据解析-----------------------------------
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
return retrofit.create(serviceClass);
}
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
//打印retrofit日志
Log.i("RetrofitLog","retrofitBack ======================= "+message);
}
});
private static final long DEFAULT_TIMEOUT = 10;
private OkHttpClient getOkHttpClient(){
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
//定制OkHttp
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
builder.writeTimeout(DEFAULT_TIMEOUT,TimeUnit.SECONDS);
builder.addInterceptor(loggingInterceptor);
//设置缓存
File httpCacheDirectory = new File(SDCardUtils.getRootDirectoryPath(),"这里是你的网络缓存存放的地址");
builder.cache(new Cache(httpCacheDirectory,10*1024*1024));
return builder.build();
}
}
好了 下一步 咱们要建一个网络请求的基类,通常网络请求返回的数据最外层的根式就是 code msg result,可变的就是result,因此咱们把result的类型定义为一个泛型的android
public class HttpResult<T> extends BaseEntity {
public int code;
private boolean isSuccess;
private T result;
private String msg;
public void setMsg(String msg) {
this.msg = msg;
}
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
public boolean isSuccess() {
return code == 200;
}
public int getCode() {
return code;
}
}
既然咱们的网络请求是rxjava配合retrofit 下面就定义咱们的网络请求订阅subscriberjson
public abstract class HttpResultSubscriber<T> extends Subscriber<HttpResult<T>> {
@Override
public void onNext(HttpResult<T> t) {
if (t.isSuccess()) {
onSuccess(t.getResult());
} else {
_onError(t.getMsg().getCode());
}
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
//在这里作全局的错误处理
if (e instanceof ConnectException ||
e instanceof SocketTimeoutException ||
e instanceof TimeoutException) {
//网络错误
_onError(-9999);
}
}
public abstract void onSuccess(T t);
public abstract void _onError(int status);
}
OK咱们的网络请求基类已经完成啦!下面开始咱们的网络请求数组
首先咱们定义一个接口(以登陆为例):缓存
public interface LoginService {
//这个例子是post为例,若是想要了解其余的网络请求,请点击文章开始出的retrofit连接
@FormUrlEncoded
@POST(Constant.LOGIN_URL) 这里是你的登陆url
//能够看到咱们的登陆返回的是一个Observable,它里面包含的使咱们的网络请求返回的实体基类,
//而咱们实体基类的result如今就是UserInfoEntity
Observable<HttpResult<UserInfoEntity>> login(@Field("mobile") String phone,
@Field("password") String pwd);
}
如今开始咱们的网络请求啦网络
public void login(String phone, String pwd) {
ServiceFactory.getInstance()
.createService(LoginService.class)
.login(phone,pwd)
.compose(TransformUtils.<HttpResult<UserInfoEntity>>defaultSchedulers())
.subscribe(new HttpResultSubscriber<UserInfoEntity>() {
@Override
public void onSuccess(UserInfoEntity userInfoEntity) {
//这是网络请求陈宫的回调
}
@Override
public void _onError(int status) {
//这是失败的回调 根据status作具体的操做
}
});
}
你觉得这样就好了 , 这样子确实没毛病,确实已经妥妥的了。但是,但是,事与愿违啊!!!async
通常状况这是咱们的返回json格式:ide
{
"code":200,
"msg":"成功",
"result":{}
}
咱们刚才定义登陆接口的时候 返回的实体基类例传入的是UserInfoEntity 这确实是没问题的 但是大家加入登陆失败的时候返回的json数据格式是这样的怎么办?
{
"code":300,
"msg":"成功",
"result":[]
}
失败的时候返回的实体又是一个数组,这样子就会抱一个json解析异常拿很少失败的状态码和提示信息post
OK其实咱们的网络请求已经完成90%了,剩下的就是不重要的失败的时候回调了。
方法一:(这是在后台兄弟好说话,并且不打人的状况下…固然这种好人,仍是有的,不过这不是咱们今天要讲的重点)
咱们可让后台返回的json数据中的result永远是个数组。
{
"code":300,
"msg":"成功",
"result":[]
}
方法二:
首先给你们看一个图片
这就是咱们要下手的地方
上面咱们添加的工厂变换器是导入的依赖 compile 'com.squareup.retrofit2:converter-gson:2.0.2' 这个提供的
那可能有人要问了,那咱们不用这个用哪一个啊,不着急,不着急。还好retrofit是支持自定义的ConverterFactory的
下面咱们就开始咱们的自定义征程吧。
一、自定义Gson响应体变换器
public class GsonResponseBodyConverter<T> implements Converter<ResponseBody,T>{
private final Gson gson;
private final Type type;
public GsonResponseBodyConverter(Gson gson,Type type){
this.gson = gson;
this.type = type;
}
@Override
public T convert(ResponseBody value) throws IOException {
String response = value.string();
//先将返回的json数据解析到Response中,若是code==200,则解析到咱们的实体基类中,不然抛异常
Response httpResult = gson.fromJson(response, Response.class);
if (httpResult.getCode()==200){
//200的时候就直接解析,不可能出现解析异常。由于咱们实体基类中传入的泛型,就是数据成功时候的格式
return gson.fromJson(response,type);
}else {
ErrorResponse errorResponse = gson.fromJson(response,ErrorResponse.class);
//抛一个自定义ResultException 传入失败时候的状态码,和信息
throw new ResultException(errorResponse.getCode(),errorResponse.getMsg());
}
}
}
二、自定义一个响应变换工厂 继承自 retrofit的 converter.Factory
public class ResponseConverterFactory extends Converter.Factory {
public static ResponseConverterFactory create() {
return create(new Gson());
}
public static ResponseConverterFactory create(Gson gson) {
return new ResponseConverterFactory(gson);
}
private final Gson gson;
private ResponseConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
//返回咱们自定义的Gson响应体变换器
return new GsonResponseBodyConverter<>(gson, type);
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
//返回咱们自定义的Gson响应体变换器
return new GsonResponseBodyConverter<>(gson,type);
}
}
而后将上面的GsonConverterFactory.create() 替换成咱们自定义的ResponseConverterFactory.create()。
public <S> S createService(Class<S> serviceClass){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constant.BASE_URL)
.client(getOkHttpClient())
//.addConverterFactory(GsonConverterFactory.create())
//而后将上面的GsonConverterFactory.create()替换成咱们自定义的ResponseConverterFactory.create()
.addConverterFactory(ResponseConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
return retrofit.create(serviceClass);
}
再而后,最后一个而后啦(-.-)
在咱们的自定义的Rxjava订阅者 subscriber中的onError()中加入咱们刚才定义的ResultException。
@Override
public void onError(Throwable e) {
e.printStackTrace();
//在这里作全局的错误处理
if (e instanceof ConnectException ||
e instanceof SocketTimeoutException ||
e instanceof TimeoutException) {
//网络错误
_onError(-9999);
} else if (e instanceof ResultException) {
//自定义的ResultException
//因为返回200,300返回格式不统一的问题,自定义GsonResponseBodyConverter凡是300的直接抛异常
_onError(((ResultException) e).getErrCode());
System.out.println("---------errorCode------->"+((ResultException) e).getErrCode());
}
}
此次是真的完成了咱们的json数据解析异常的处理,其实咱们的解决办法是解析了两次,第一次解析的时候咱们的Response中只有只是解析了最外层的 code 和 msg ,result中的是没有解析的。response中的code==200,直接将数据解析到咱们的实体基类中。若是code!=200时,直接抛自定义的异常,直接会回调到subscriber中的onError()中。虽然进行了两次解析,可是第一次只是解析了code,和msg 对于效率的影响其实并不大,在功能实现的基础上一点点效率的影响(并且这个影响是微乎其微的-.-)其实无伤大雅的。