每一個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 對於效率的影響其實並不大,在功能實現的基礎上一點點效率的影響(並且這個影響是微乎其微的-.-)其實無傷大雅的。