zoukankan      html  css  js  c++  java
  • Android Retrofit+RxJava 优雅的处理服务器返回异常、错误

    标签:

    开始本博客之前,请先阅读: 
    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 == 1001){
                                //.....
                            }else if(data.state == 1002){
    
                            }
                        }
                    });

    现在我希望在发生任何错误的情况下,都会调用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 != 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) {
                //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,123));
            }
        }
    
        /**
         * 错误回调
         */
        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 = 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){             //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 = 1000;
        /**
         * 解析错误
         */
        public static final int PARSE_ERROR = 1001;
        /**
         * 网络错误
         */
        public static final int NETWORD_ERROR = 1002;
        /**
         * 协议出错
         */
        public static final int HTTP_ERROR = 1003;
    }
    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();
            }
        }
    
    }
  • 相关阅读:
    最近几天在winform系统界面操作,流程操作上的经验总结.
    人生路痛苦中......
    Contact List
    奥巴马当选
    在会计电算化软件中设计凭证数据库的几种方法转摘(粗浅)
    修改ListItem时出现Error: Invalid data has been used to update the list item. The field you are trying to update may be read only.
    MOSS误删网站集后,如何还原?
    SharePoint2007 管理中心TimerJob”计时器作业定义”页面报错
    SharePoint 2010 常用技巧及方法总结
    利用RunWithElevatedPrivileges模拟管理员权限时慎用SPContext
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/7288878.html
Copyright © 2011-2022 走看看