标签:

开始本博客之前,请先阅读: 
Retrofit请求数据对错误以及网络异常的处理

异常&错误

实际开发经常有这种情况,比如登录请求,接口返回的 
信息包括请求返回的状态:失败还是成功,错误码,User对象等等。如果网络等原因引起的登录失败可以归结为异常,如果是用户信息输入错误导致的登录失败算是错误。

假如服务器返回的是统一数据格式:

/**
* 标准数据格式
* @param <T>
*/
public class Response<T> {
public int state;
public String message;
public T data;
}
  • 网络异常导致的登录失败,在使用Retrofit+RxJava请求时都会直接调用subscribe的onError事件;
  • 密码错误导致的登录失败,在使用Retrofit+RxJava请求时都会调用subscribe的onNext事件;

无论是异常还是错误,都要在subscribe里面处理异常信息,如下代码:

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 == ){
//.....
}else if(data.state == ){ }
}
});

现在我希望在发生任何错误的情况下,都会调用onError事件,并且由model来处理错误信息。那么,此时我们就应该有一个ExceptionEngine来处理事件流中的错误信息了。

在工作流中处理异常

在Retrofit+RxJava的框架下,我们获取网络数据的流程通常如下:

订阅->请求接口->数据解析->更新UI

整个数据请求过程都是发生在Rx中的工作流之中。当有异常产生的时候,我们要尽量不在ui层里面进行判断,换句话说,我们没有必要去告诉ui层具体的错误信息,只需要让他弹出一个信息(Toast或者Dialog)展示我们给它的信息就行。

请求接口和数据解析都可能出错,所以在这两层进行错误处理。为了更好的解耦,我们通过拦截器拦截错误,然后根据错误类型分发信息。

拦截器

数据解析层的拦截器

这个拦截器主要是为了获取具体的错误信息,分发给上层的UI,给用户以提示,增强用户体验。

public Observable<Weather> getWeather(String cityName){
return weatherService.getWeather(cityName)
//拦截服务器返回的错误
.map(new ServerResponseFunc<Weather>())
//HttpResultFunc()为拦截onError事件的拦截器,后面会讲到,这里先忽略
.onErrorResumeNext(new HttpResponseFunc<Weather>());
}
//拦截固定格式的公共数据类型Response<T>,判断里面的状态码
private class ServerResponseFunc<T> implements Func1<Response<T>, T> {
@Override
public T call(Response<T> reponse) {
//对返回码进行判断,如果不是0,则证明服务器端返回错误信息了,便根据跟服务器约定好的错误码去解析异常
if (reponse.state != ) {
//如果服务器端有错误信息返回,那么抛出异常,让下面的方法去捕获异常做统一处理
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) {
//ExceptionEngine为处理异常的驱动器
return Observable.error(ExceptionEngine.handleException(throwable));
}
}

两个拦截器以前使用,代码如下:

public Observable<Weather> getWeather(String cityName){
return weatherService.getWeather(cityName)
//拦截服务器返回的错误
.map(new ServerResponseFunc<Weather>())
//HttpResponseFunc()为拦截onError事件的拦截器
.onErrorResumeNext(new HttpResponseFunc<Weather>());
}

相关类:

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 Subscriber<T> {
@Override
public void onError(Throwable e) {
if(e instanceof ApiException){
onError((ApiException)e);
}else{
onError(new ApiException(e,));
}
} /**
* 错误回调
*/
protected abstract void onError(ApiException ex);
}

处理异常的驱动器

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 { //对应HTTP的状态码
private static final int UNAUTHORIZED = ;
private static final int FORBIDDEN = ;
private static final int NOT_FOUND = ;
private static final int REQUEST_TIMEOUT = ;
private static final int INTERNAL_SERVER_ERROR = ;
private static final int BAD_GATEWAY = ;
private static final int SERVICE_UNAVAILABLE = ;
private static final int GATEWAY_TIMEOUT = ; public static ApiException handleException(Throwable e){
ApiException ex;
if (e instanceof HttpException){ //HTTP错误
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 = ;
/**
* 解析错误
*/
public static final int PARSE_ERROR = ;
/**
* 网络错误
*/
public static final int NETWORD_ERROR = ;
/**
* 协议出错
*/
public static final int HTTP_ERROR = ;
}
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

public class DialogHelper {
/**
* 通用Dialog
*
*/
// 因为本类不是activity所以通过继承接口的方法获取到点击的事件
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);
// 设置title
builder.setTitle(title);
// 设置确定按钮,固定用法声明一个按钮用这个setPositiveButton
builder.setPositiveButton(ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// 如果确定被电击
if (listenerYes != null) {
listenerYes.onOkClick();
}
mDialog = null;
}
});
// 设置取消按钮,固定用法声明第二个按钮要用setNegativeButton
builder.setNegativeButton(cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// 如果取消被点击
if (listenerNo != null) {
listenerNo.onCancelClick();
}
mDialog = null;
}
}); // 控制这个dialog可不可以按返回键,true为可以,false为不可以
builder.setCancelable(false);
// 显示dialog
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();
}
} }

Android Retrofit+RxJava 优雅的处理服务器返回异常、错误的更多相关文章

  1. 给Apache加载rewrite模块后,服务器返回500错误,以及a2enmod命令

    我的机子是Ubuntu. 今天想给url做一个rewrite,让url看起来更漂亮一点.在Apache配置文件(我的是 /etc/apache/apache2.conf)文件中已经把AllOverri ...

  2. system.net.webexception远程服务器返回了错误: NotFound。

    Not Found类的错误主要是由于网络服务访问出错.所以需要分析是由哪个网络服务访问失败而导致的. DataAccessSilverlight.PowerDataServiceReference.G ...

  3. Silverlight提示“Load 操作失败。远程服务器返回了错误: NotFound”

    调试时出现“Load 操作失败.远程服务器返回了错误: NotFound”: 一定要注意此错误之前的错误是什么?基本就是用户Cookie的问题,用户没有登录. 有时需要设置成Any CPU 有时重新编 ...

  4. iOS AFNetworking 打印从服务器返回的错误提示信息

    每次做项目的时候都会在网络请求时候测试接口的时候会出现一些不同的错误,而控制台打印的错误提示信息都是data类型,看不出提示的错误的信息是什么.后面经过一些查阅发现其实是可以把这个转变为string的 ...

  5. Android Retrofit RxJava实现缓存

    RxJava如何与Retrofit结合参考:http://blog.csdn.net/jdsjlzx/article/details/52015347 缓存配置 app网络数据的离线缓存实现有很多种办 ...

  6. delphi http 403 获取不到服务器返回的错误消息 用浏览器打开url可以返回

    用delphi的idhttp Get一个url如下: http://117.135.237.4:9090/agent/api/treatmentModeUpdate?userName=VDAwMIMQ ...

  7. 78. Android之 RxJava 详解

    转载:http://gank.io/post/560e15be2dca930e00da1083 前言 我从去年开始使用 RxJava ,到现在一年多了.今年加入了 Flipboard 后,看到 Fli ...

  8. 写自动更新程序出现"远程服务器返回错误: (404) 未找到"

    在win2003配置后,在客户端运行时能够下载exe和dll文件,但是在更新lib文件时总是报“远程服务器返回错误: (404) 未找到”错误,不明白咋会出现这个问题,去网上一查,发现以下解决办法: ...

  9. [Android] 转-RxJava+MVP+Retrofit+Dagger2+Okhttp大杂烩

    原文url: http://blog.iliyun.net/2016/11/20/%E6%A1%86%E6%9E%B6%E5%B0%81%E8%A3%85/ 这几年来android的网络请求技术层出不 ...

随机推荐

  1. vue1.0父子、兄弟间 通信案例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. GPU Command Buffer

    For Developers‎ > ‎Design Documents‎ > ‎ GPU Command Buffer This are mostly just notes on the ...

  3. 什么是Node.js?

     Node.js是一个基于Chrome JavaScript运行时建立的平台, 用于方便地搭建响应速度快.易于扩展的网络应用.Node.js 使用事件驱动, 非阻塞I/O 模型而得以轻量和高效,非常适 ...

  4. NOIP2017普及组题

    提高组死亡选手来AK普及(耗时两天). T1 #include<bits/stdc++.h> using namespace std; int A,B,C; int main() { ci ...

  5. python -迭代器与生成器 以及 iterable(可迭代对象)、yield语句

    我刚开始学习编程没多久,对于很多知识还完全不知道,而有些知道的也是一知半解,我想把学习到的知识记录下来,一是弥补记忆力差的毛病,二也是为了待以后知识能进一步理解透彻时再回来做一个补充. 参考链接: 完 ...

  6. FOJ1205 小鼠迷宫问题 (BFD+递推)

    FOJ1205 小鼠迷宫问题 (BFD+递推) 小鼠a与小鼠b身处一个m×n的迷宫中,如图所示.每一个方格表示迷宫中的一个房间.这m×n个房间中有一些房间是封闭的,不允许任何人进入.在迷宫中任何位置均 ...

  7. 15 hbase 学习(十五)缓存机制以及可以利用SSD作为存储的BucketCache

    下面介绍Hbase的缓存机制:  a.HBase在读取时,会以Block为单位进行cache,用来提升读的性能 b.Block可以分类为DataBlock(默认大小64K,存储KV).BloomBlo ...

  8. ArcGIS api for javascript——合并两个ArcGIS Online服务

    描述 这个示例创建一个地图并ArcGIS Online增加连个图层到地图.ArcGIS Online是由ESRI体提供的一组切片地图服务,可以用来通过高质量的地图和数据增强应用.这个示例增加影像和运输 ...

  9. Docker入门实践(三) 基本操作

    Docker安装完毕.我们就能够试着来执行一些命令了.看看docker能够干什么. (一) 创建一个容器 首先.让我们执行一个最简单的容器,hello-world.假设安装没有问题.并执行正确的话,应 ...

  10. 杭电 HDU ACM 2795 Billboard(线段树伪装版)

    Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total ...