开始本博客之前,请先阅读: Retrofit请求数据对错误以及网络异常的处理
异常&错误
实际开发经常有这种情况,比如登录请求,接口返回的 信息包括请求返回的状态:失败还是成功,错误码,User对象等等。如果网络等原因引起的登录失败可以归结为异常,如果是用户信息输入错误导致的登录失败算是错误。
假如服务器返回的是统一数据格式:
public class Response<T> {
public int state;
public String message;
public T data;}
网络异常导致的登录失败,在使用Retrofit+RxJava请求时都会直接调用subscribe的onError事件;
密码错误导致的登录失败,在使用Retrofit+RxJava请求时都会调用subscribe的onNext事件;
无论是异常还是错误,都要在subscribe里面处理异常信息,如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
APIWrapper.getInstance().login(
"username" ,
"password" ) .subscribe(
new Observer<Response<User>>() {
@Override public void onCompleted () { }
@Override public void onError (Throwable e) { }
@Override public void onNext (Response<User> data) {
if (data.state ==
1001 ){ }
else if (data.state ==
1002 ){ } } });
现在我希望在发生任何错误的情况下,都会调用onError事件,并且由model来处理错误信息。那么,此时我们就应该有一个ExceptionEngine来处理事件流中的错误信息了。
在工作流中处理异常
在正常情况下,我们获取网络数据的流程通常如下:
请求接口->解析数据->更新UI
整个数据请求过程都是发生在Rx中的工作流之中。当有异常产生的时候,我们要尽量不在ui层里面进行判断,换句话说,我们没有必要去告诉ui层具体的错误信息,只需要让他弹出一个信息(Toast或者Dialog)展示我们给它的信息就行。
请求接口和数据解析都可能出错,所以在这两层进行错误处理。为了更好的解耦,我们通过拦截器拦截错误,然后根据错误类型分发信息。
拦截器
数据解析层的拦截器
这个拦截器主要是为了获取具体的错误信息,分发给上层的UI,给用户以提示,增强用户体验。
public Observable<Weather>
getWeather (String cityName){
return weatherService.getWeather(cityName) .map(
new ServerResponseFunc<Weather>()) .onErrorResumeNext(
new HttpResponseFunc<Weather>()); }
1
2
3
4
5
6
7
8
9
10
11
12
13
private class ServerResponseFunc <T > implements Func1 <Response <T >, T > { @Override
public T call(Response<T> reponse) {
if (reponse.state !=
0 ) {
throw new ServerException(reponse.state,reponse.message); }
return reponse.data; } }
所以整个逻辑是这样的:
所以在前三步的过程中,只要发生异常(服务器返回的错误也抛出了)都会抛出,这时候就触发了RxJava的OnError事件。
处理onError事件的拦截器
这个拦截器主要是将异常信息转化为用户”能看懂”的友好提示。
private class HttpResponseFunc <T > implements Func1 <Throwable , Observable <T >> { @Override
public Observable<T> call(Throwable throwable) {
return Observable.error(ExceptionEngine.handleException(throwable)); } }
两个拦截器以前使用,代码如下:
public Observable<Weather>
getWeather (String cityName){
return weatherService.getWeather(cityName) .map(
new ServerResponseFunc<Weather>()) .onErrorResumeNext(
new HttpResponseFunc<Weather>()); }
调用:
APIWrapper.getInstance().getWeather(
"北京" ) .subscribe(
new SampleProgressObserver<Weather>(MainActivity.
this ) {
@Override public void onNext (WeatherBean weatherBean) { tv.setText(weatherBean.toString()); } });
相关类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public
class RxSubscriber <T > extends ErrorSubscriber <T > { @Override public void onStart() {
super .onStart(); DialogHelper.showProgressDlg(context,
"正在加载数据" ); }
@Override public void onCompleted() { DialogHelper.stopProgressDlg(); }
@Override protected void onError(ApiException ex) { DialogHelper.stopProgressDlg(); Toast.makeText(context, ex.message, Toast.LENGTH_SHORT).show(); }
@Override public void onNext(T t) { }}public
abstract class ErrorSubscriber <T > extends Observer <T > { @Override public void onError(Throwable e) {
if (e instanceof ApiException){ onError((ApiException)e); }
else { onError(
new ApiException(e,
123 )); } }
/** * 错误回调 */ protected abstract void onError(ApiException ex);}
处理异常的驱动器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package com.sanniuben.net;
import android.net.ParseException;
import com.google.gson.JsonParseException;
import org.json.JSONException;
import java.net.ConnectException;
import retrofit2.adapter.rxjava.HttpException;
/** * Created by Lzx on 2016/7/11. */ public class ExceptionEngine { 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 ApiException
handleException (Throwable e){ ApiException ex;
if (e
instanceof HttpException){ HttpException httpException = (HttpException) e; ex =
new ApiException(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 ApiException(resultException, resultException.code); ex.message = resultException.message;
return ex; }
else if (e
instanceof JsonParseException || e
instanceof JSONException || e
instanceof ParseException){ ex =
new ApiException(e, ERROR.PARSE_ERROR); ex.message =
"解析错误" ;
return ex; }
else if (e
instanceof ConnectException){ ex =
new ApiException(e, ERROR.NETWORD_ERROR); ex.message =
"连接失败" ;
return ex; }
else { ex =
new ApiException(e, ERROR.UNKNOWN); ex.message =
"未知错误" ;
return ex; } }}
/** * 约定异常 */ public 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 ;}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ApiException extends Exception { public int code;
public String message;
public ApiException (Throwable throwable,
int code) {
super (throwable);
this .code = code; }}
public class ServerException extends RuntimeException { public int code;
public String message;}
DialogHelper.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
public class DialogHelper { /** * 通用Dialog * */ public interface OnOkClickListener { abstract void onOkClick(); }
/** * Listener */ public interface OnCancelClickListener { abstract void onCancelClick(); }
private static AlertDialog mDialog;
public static void showDialog (Context context, String title, String content,
final OnOkClickListener listenerYes,
final OnCancelClickListener listenerNo) { showDialog(context, context.getString(android.R.string.ok), context.getString(android.R.string.cancel), title, content, listenerYes, listenerNo); }
public static void showDialog (Context context, String ok, String cancel, String title, String content,
final OnOkClickListener listenerYes,
final OnCancelClickListener listenerNo) { AlertDialog.Builder builder =
new AlertDialog.Builder(context); builder.setMessage(content); builder.setTitle(title); builder.setPositiveButton(ok,
new DialogInterface.OnClickListener() {
public void onClick (DialogInterface dialog,
int which) {
if (listenerYes !=
null ) { listenerYes.onOkClick(); } mDialog =
null ; } }); builder.setNegativeButton(cancel,
new DialogInterface.OnClickListener() {
public void onClick (DialogInterface dialog,
int which) {
if (listenerNo !=
null ) { listenerNo.onCancelClick(); } mDialog =
null ; } }); builder.setCancelable(
false ); mDialog = builder.create();
if (!mDialog.isShowing()) mDialog.show(); }
public static void showDialog (Context context,
int ok,
int cancel,
int title,
int content,
final OnOkClickListener listenerYes,
final OnCancelClickListener listenerNo) { showDialog(context, context.getString(ok), context.getString(cancel), context.getString(title), context.getString(content), listenerYes, listenerNo); }
static ProgressDialog progressDlg =
null ;
/** * 启动进度条 * * @param strMessage 进度条显示的信息 * @param // 当前的activity */ public static void showProgressDlg (Context ctx, String strMessage) {
if (
null == progressDlg) {
if (ctx ==
null )
return ; progressDlg =
new ProgressDialog(ctx); progressDlg.setProgressStyle(ProgressDialog.STYLE_SPINNER); progressDlg.setMessage(strMessage); progressDlg.setIndeterminate(
false ); progressDlg.setCancelable(
true ); progressDlg.show(); } }
public static void showProgressDlg (Context ctx) { showProgressDlg(ctx,
"" ); }
/** * 结束进度条 */ public static void stopProgressDlg () {
if (
null != progressDlg && progressDlg.isShowing()) { progressDlg.dismiss(); progressDlg =
null ; }
if (
null != dialog && dialog.isShowing()) { dialog.dismiss(); dialog =
null ; } }
private static Dialog dialog;
public static void showDialogForLoading (Context context, String msg,
boolean cancelable) {
if (
null == dialog) {
if (
null == context)
return ; View view = LayoutInflater.from(context).inflate(R.layout.layout_loading_dialog,
null ); TextView loadingText = (TextView)view.findViewById(R.id.loading_tip_text); loadingText.setText(msg); dialog =
new Dialog(context, R.style.loading_dialog_style); dialog.setCancelable(cancelable); dialog.setCanceledOnTouchOutside(cancelable); dialog.setContentView(view,
new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); Activity activity = (Activity) context;
if (activity.isFinishing())
return ; dialog.show(); } }}
可能本博客也不是最好的解决方案,如果有更好的想法,我愿与你互相交流!
代码参考:https://github.com/122627018/Retorfit_RxJava_Exception
分享: Retrofit+RxJava错误预处理
看到bobo_wang的文章,不仅感觉有受益匪浅,这里做下介绍。
首先定义如下Transformer转换器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static <T> Observable.Transformer<Response<T>, T> sTransformer() {
return responseObservable -> responseObservable.map(tResponse -> {
if (!tResponse.success)
throw new RuntimeException(tResponse.code);
return tResponse.data; }).onErrorResumeNext(
new HttpResponseFunc<>()); }
public static <T> Observable.Transformer<T, T> switchSchedulers() {
return observable -> observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); }
private static class HttpResponseFunc <T > implements Func1 <Throwable , Observable <T >> { @Override
public Observable<T> call(Throwable throwable) {
return Observable.error(
new Throwable(throwable)); } }
调用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void login (View v){ apiservice.login(name,pwd) .compose(Transformers.sTransformer()) .compose(Transformers.switchSchedulers()) .subscribe(subscriber);}
private Subscriber<UserModel> subscriber =
new Subscriber<UserModel>() {
@Override public void onCompleted () { }
@Override public void onError (Throwable e) { }
@Override public void onNext (UserModel model) { } };
接口:
@FormUrlEncoded @POST("interface?login" ) Observable<Response<UserModel>> login(@Field("name" ) String name,@Field("pwd" ) String pwd) ;
最后再来点干货。
Transformer 和 Func 处理的区别
如上的处理,定义了 一个 sTransformer 和一个 HttpResponseFunc, 从中可以明显感觉的到sTransformer其实也是可以用Func1来定义的,
public void login (View v){ apiservice.login(name,pwd) .compose(Transformers.switchSchedulers()) .map(
new TransFuc<UserModel>()) .onErrorReturn(
new HttpResponseFunc<>()) .subscribe(subscriber);}
public static class TransFuc <T > implements Func1 <Response <T >, T > { @Override
public T call(Response<T> tResponse) {
if (!tResponse.success)
throw new RuntimeException(tResponse.code);
return tResponse.data; } }
Transformer作用于整个流,Func1是一个操作符,作用于数据项。
不规范数据的处理
有时候服务器返回的数据并不是十分规范的,比如 正常返回是这样的
{
"success" :
true ,
"status" :
"1" ,
"data" : { } }
错误时时这样的
{
"success" :
false ,
"status" :
"0" ,
"data" :
"371" }
这时候如果我么用泛型处理
public class Response<T> {
public boolean success;
public String status;
public T data;}
针对这种数据,我们的泛型该怎么写成了问题,错误的时候是String,正确的时候是Bean?
如果我们直接写成JavaBean,那么我们会得到一个错误,LinkedTreeMap cannot be cast to xxx 类型转换错误.这时候只能将泛型写成String来处理了,使用如下Transformer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static Observable
. Transformer
< String , UserModel
> trans() {
return stringObservable
-> stringObservable
. map (s
-> { Response parseJson
= GsonUtil
. parseJson(s, Response
. class);
if (
null == parseJson) { throw
new RuntimeException(
"null == parseJson" ); }
if (PatternsUtil
. isNum(parseJson
. data . toString())) { throw
new RuntimeException(parseJson
. data . toString()); }
return GsonUtil
. parseJson(s, UserModel
. class); })
. onErrorResumeNext(
new HttpResponseFunc
<> ()); }
使用就变成了如下这样
public void login(View v){ apiservice
.login (name,pwd)
.compose (Transformers
.switchSchedulers ())
.compose (Transformers
.trans ())
.subscribe (subscriber)}
封装的越来越简介了,用到实际项目吧!