Rxjava、Retrofit返回json數(shù)據(jù)解析異常處理

每個App都避免不了要進行網(wǎng)絡請求媳谁,從最開始的用谷歌封裝的volley到再到android-async-http再到OKHttpUtils再到現(xiàn)在的RetrofitRxJava,從我自己用后的體驗來看,用了retrofit和RxJava真的回不去了,回不去了司草,不去了授段,去了,了...(哈哈涩哟,本來還想分析下這四個的區(qū)別索赏,網(wǎng)上這樣的文章很多,我就沒必要多添亂了-.-)贴彼。不多逼逼潜腻,下面開始正文。

RxJava的學習資料

RxJava javadoc

ReactiveX文檔中文翻譯

1器仗、Rxjava和Retrofit依賴導入:

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' //打印網(wǎng)絡請求的log日志

2融涣、網(wǎng)絡請求基類的配置

建立一個工廠類

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工廠變換器也就是不用管數(shù)據(jù)解析-----------------------------------
.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(),"這里是你的網(wǎng)絡緩存存放的地址");
builder.cache(new Cache(httpCacheDirectory,10*1024*1024));

return builder.build();
}

}

好了 下一步 我們要建一個網(wǎng)絡請求的基類,一般網(wǎng)絡請求返回的數(shù)據(jù)最外層的根式就是 code msg result青灼,可變的就是result暴心,所以我們把result的類型定義為一個泛型的

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;
}
}

既然我們的網(wǎng)絡請求是rxjava配合retrofit 下面就定義我們的網(wǎng)絡請求訂閱subscriber

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) {
//網(wǎng)絡錯誤
_onError(-9999);
}
}


public abstract void onSuccess(T t);

public abstract void _onError(int status);
}

OK我們的網(wǎng)絡請求基類已經完成啦!下面開始我們的網(wǎng)絡請求

首先我們定義一個接口(以登錄為例):

public interface LoginService {

//這個例子是post為例杂拨,如果想要了解其他的網(wǎng)絡請求专普,請點擊文章開始出的retrofit鏈接
@FormUrlEncoded
@POST(Constant.LOGIN_URL) 這里是你的登錄url

//可以看到我們的登錄返回的是一個Observable,它里面包含的使我們的網(wǎng)絡請求返回的實體基類弹沽,

//而我們實體基類的result現(xiàn)在就是UserInfoEntity
Observable<HttpResult<UserInfoEntity>> login(@Field("mobile") String phone,
@Field("password") String pwd);
}

現(xiàn)在開始我們的網(wǎng)絡請求啦

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) {
//這是網(wǎng)絡請求陳宮的回調
}

@Override
public void _onError(int status) {
//這是失敗的回調 根據(jù)status做具體的操作
}
});
}

你以為這樣就行了?檀夹,? 這樣子確實沒毛病筋粗,確實已經妥妥的了≌ǘ桑可是娜亿,可是,事與愿違鞍龆隆B蚓觥!吼畏!

3督赤、具體解決辦法

一般情況這是我們的返回json格式:

{
"code":200,
"msg":"成功",
"result":{}
}

我們剛才定義登錄接口的時候 返回的實體基類例傳入的是UserInfoEntity? 這確實是沒問題的 可是你們加入登錄失敗的時候返回的json數(shù)據(jù)格式是這樣的怎么辦?

{
"code":300,
"msg":"成功",
"result":[]
}

失敗的時候返回的實體又是一個數(shù)組泻蚊,這樣子就會抱一個json解析異常拿不多失敗的狀態(tài)碼和提示信息

OK其實我們的網(wǎng)絡請求已經完成90%了躲舌,剩下的就是不重要的失敗的時候回調了。

方法一:(這是在后臺兄弟好說話性雄,而且不打人的情況下...當然這種好人没卸,還是有的,不過這不是我們今天要講的重點)

我們可以讓后臺返回的json數(shù)據(jù)中的result永遠是個數(shù)組秒旋。

{
"code":300,
"msg":"成功",
"result":[]
}

方法二:

??????? 首先給大家看一個圖片

這就是我們要下手的地方

上面我們添加的工廠變換器是導入的依賴 compile 'com.squareup.retrofit2:converter-gson:2.0.2' 這個提供的约计,

那可能有人要問了,那我們不用這個用哪個啊迁筛,不著急病蛉,不著急。還好retrofit是支持自定義的ConverterFactory的

下面我們就開始我們的自定義征程吧瑰煎。

---------------------------------------------------------華麗麗的分割線-----------------------------------------------

1、自定義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數(shù)據(jù)解析到Response中俗孝,如果code==200酒甸,則解析到我們的實體基類中,否則拋異常
Response httpResult = gson.fromJson(response, Response.class);
if (httpResult.getCode()==200){
//200的時候就直接解析赋铝,不可能出現(xiàn)解析異常插勤。因為我們實體基類中傳入的泛型,就是數(shù)據(jù)成功時候的格式
return gson.fromJson(response,type);
}else {
ErrorResponse errorResponse = gson.fromJson(response,ErrorResponse.class);
//拋一個自定義ResultException 傳入失敗時候的狀態(tài)碼革骨,和信息
throw new ResultException(errorResponse.getCode(),errorResponse.getMsg());
}
}
}

2农尖、自定義一個響應變換工廠 繼承自 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()

然后將上面的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) {
//網(wǎng)絡錯誤
_onError(-9999);
} else if (e instanceof ResultException) {
//自定義的ResultException
//由于返回200,300返回格式不統(tǒng)一的問題,自定義GsonResponseBodyConverter凡是300的直接拋異常
_onError(((ResultException) e).getErrCode());
System.out.println("---------errorCode------->"+((ResultException) e).getErrCode());
}
}

這次是真的完成了我們的json數(shù)據(jù)解析異常的處理筑凫,其實我們的解決辦法是解析了兩次滑沧,第一次解析的時候我們的Response中只有只是解析了最外層的 code 和 msg? 并村,result中的是沒有解析的。response中的code==200滓技,直接將數(shù)據(jù)解析到我們的實體基類中哩牍。如果code!=200時令漂,直接拋自定義的異常膝昆,直接會回調到subscriber中的onError()中。雖然進行了兩次解析叠必,但是第一次只是解析了code荚孵,和msg 對于效率的影響其實并不大,在功能實現(xiàn)的基礎上一點點效率的影響(而且這個影響是微乎其微的-.-)其實無傷大雅的挠唆。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末处窥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子玄组,更是在濱河造成了極大的恐慌滔驾,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俄讹,死亡現(xiàn)場離奇詭異哆致,居然都是意外死亡,警方通過查閱死者的電腦和手機患膛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門摊阀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人踪蹬,你說我怎么就攤上這事胞此。” “怎么了跃捣?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵漱牵,是天一觀的道長。 經常有香客問我疚漆,道長酣胀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任娶聘,我火速辦了婚禮闻镶,結果婚禮上,老公的妹妹穿的比我還像新娘丸升。我一直安慰自己铆农,他們只是感情好,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布狡耻。 她就那樣靜靜地躺著顿涣,像睡著了一般波闹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涛碑,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天精堕,我揣著相機與錄音,去河邊找鬼蒲障。 笑死歹篓,一個胖子當著我的面吹牛,可吹牛的內容都是我干的揉阎。 我是一名探鬼主播庄撮,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼毙籽!你這毒婦竟也來了洞斯?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤坑赡,失蹤者是張志新(化名)和其女友劉穎烙如,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毅否,經...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡亚铁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了螟加。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片徘溢。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖捆探,靈堂內的尸體忽然破棺而出然爆,到底是詐尸還是另有隱情,我是刑警寧澤黍图,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布施蜜,位于F島的核電站,受9級特大地震影響雌隅,放射性物質發(fā)生泄漏。R本人自食惡果不足惜缸沃,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一恰起、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧趾牧,春花似錦检盼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蹦渣。三九已至,卻和暖如春貌亭,著一層夾襖步出監(jiān)牢的瞬間柬唯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工圃庭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锄奢,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓剧腻,卻偏偏與公主長得像拘央,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子书在,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內容