How to Use Retrofit+RxJava to Realize Exception Handling of Network Request

  • 2021-07-18 07:56:20
  • OfStack

Usually, when we communicate with the server, we will not make mistakes if we don't make a decision, and sometimes other errors will occur. At this time, we only need to agree with the server on various exceptions, and judge whether it is an execution error or a return of normal data. The specific thinking is roughly like this. Here we define ExceptionHandle, here I refer to things on the Internet, and then make a little change.

ExceptionHandle


public class ExceptionHandle {

  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 ResponseException handleException(Throwable e){
    // Convert to ResponseException, Determine error message according to status code 
    ResponseException ex;
    if(e instanceof HttpException){
      HttpException httpException=(HttpException)e;
      /**
       *  Pass in the status code, and determine the error message according to the status code 
       */
      ex=new ResponseException(e,ERROR.HTTP_ERROR);
      switch (httpException.code()){
        case UNAUTHORIZED:
          ex.message=" Not validated ";
          break;
        case FORBIDDEN:
          ex.message=" Service inhibits access ";
          break;
        case NOT_FOUND:
          ex.message=" Service does not exist ";
          break;
        case REQUEST_TIMEOUT:
          ex.message=" Request timeout ";
          break;
        case GATEWAY_TIMEOUT:
          ex.message=" Gateway timeout ";
          break;
        case INTERNAL_SERVER_ERROR:
          ex.message=" Server internal error ";
          break;
        case BAD_GATEWAY:

          break;
        case SERVICE_UNAVAILABLE:
          break;
        default:
          ex.message = " Network error ";
          break;
      }
      return ex;
    }else if(e instanceof JsonParseException
        || e instanceof JSONException
        || e instanceof ParseException){
      ex=new ResponseException(e,ERROR.PARSE_ERROR);
      ex.message=" Parsing error ";
      return ex;
    }else if(e instanceof ConnectException){
      ex=new ResponseException(e,ERROR.NETWORD_ERROR);
      ex.message=" Connection failed ";
      return ex;
    }else if(e instanceof javax.net.ssl.SSLHandshakeException){
      ex=new ResponseException(e,ERROR.SSL_ERROR);
      ex.message=" Certificate verification failed ";
      return ex;
    }else {
      ex=new ResponseException(e,ERROR.UNKNOWN);
      ex.message=" Unknown error ";
      return ex;
    }
  }
  /**
   *  Convention exception 
   */
 public static  class ERROR{
    /**
     *  Custom exception 
     */
    private static final int UNAUTHORIZED = 401;// Request user to authenticate 
    private static final int UNREQUEST=403;// The server understands the request to the client, but refuses to execute the request 
    private static final int UNFINDSOURCE=404;// The server could not find the resource based on the client's request 
    private static final int SEVERERROR=500;// Server internal error, unable to complete request. 
    /**
     *  Protocol error 
     */
    public static final int HTTP_ERROR = 1003;
    /**
     *  Unknown error 
     */
    public static final int UNKNOWN = 1000;
    /**
     *  Parsing error 
     */
    public static final int PARSE_ERROR = 1001;
    /**
     *  Network error 
     */
    public static final int NETWORD_ERROR = 1002;
    /**
     *  Certificate error 
     */
    public static final int SSL_ERROR = 1005;
  }
  /**
   *  Customize Throwable
   */
  public static class ResponseThrowable extends Exception{
    public int code;
    public String message;
    public ResponseThrowable(Throwable throwable,int code){
      super(throwable);
      this.code=code;
    }
  }
  /**
   *  Server exception 
   */
  public class ServerException extends RuntimeException{
    public int code;
    public String message;
  }

  /**
   *  Unified 1 Exception class for easy handling 
   */
  public static class ResponseException extends Exception{
    public int code;
    public String message;
    public ResponseException (Throwable throwable,int code){
      super(throwable);
      this.code=code;
    }
  }
}

Then I defined an Observer myself


public abstract class BaseObserver<T> implements Observer<T> {
  private Context context;

  public BaseObserver(Context context){
    this.context=context;
  }

  @Override
  public void onSubscribe(Disposable d) {

  }
  @Override
  public void onNext(T t) {

  }
  @Override
  public void onError(Throwable e) {
    if(e instanceof ExceptionHandle.ResponseException){
      onError((ExceptionHandle.ResponseException)e);
    }else{
      onError(new ExceptionHandle.ResponseException(e,ExceptionHandle.ERROR.UNKNOWN));
    }
  }
  @Override
  public void onComplete() {

  }
  public abstract void onError(ExceptionHandle.ResponseException exception);
}

When an error occurs here, Observerble calls onError (Throwable e) first, and continues to call custom onError, as I put it.

So when we judge the return result of the server and when it is time to issue an exception, please continue to look down:

Here we intend to use ObservableTransformer, and Transformer is actually a definite transformation of Observable.

Look at the code first:


  public static class HandleFuc<T> implements Function<UserGuideSoftConfigRForm<UserGuideSoftConfigPageInfo<List<UserguideSoftConfig>>>, T> {
    @Override
    public T apply(UserGuideSoftConfigRForm<UserGuideSoftConfigPageInfo<List<UserguideSoftConfig>>> Response) {
      if (!Response.getCode().equals("200")){
        Throwable e=new Throwable(" Contract error ");
        /**
         *  You can return different prompt information according to different status 
         *  Contract with server to return exception information 
         */
        ExceptionHandle.ResponseException responseException = new ExceptionHandle.ResponseException(e, ExceptionHandle.ERROR.HTTP_ERROR);
        return (T) Observable.error(responseException);// Throw an error exception 
      }
      return (T) Observable.just(Response);// Send out server data and return Observable<Response>
    }
  }

  // Handle erroneous transformations 
  public static class ErrorTransformer<T> implements ObservableTransformer {
    @Override
    public Observable<T> apply(Observable upstream) {
      return (Observable<T>) upstream.flatMap(new HandleFuc<T>());//flatMap Will recreate 1 A Observable, When it finishes processing the event, it will import the original Observable Object. 
    }
  }

Our HandleFuc is actually returned to the server to judge the results, the logic is very simple, the error will throw an exception to directly execute the error method. If there are no errors, normal data is sent. The first point of the value here is that flatMap recreates an Observable, and when it finishes processing the event, it re-imports the original Observerble and starts sending the event.

It is actually very simple to use:


@Provides
  ErrorTransformer provideErrorTransformer(){
    return new ErrorTransformer();
  }

 public Observable<UserGuideSoftConfigRForm<UserGuideSoftConfigPageInfo<List<UserguideSoftConfig>>>> getApplication(PageParmForm pageParmForm){
    return retrofit.create(Service.class)
        .getApplicationList(pageParmForm)
        .compose(errorTransformer)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread());
  }

Wrap it directly with compose method.

Finally, look at Activity:


new NetRepository().getApplication(new PageParmForm(Constant.orderStr,Constant.pageNum,Constant.pageSize))
    .subscribe(new BaseObserver<UserGuideSoftConfigRForm<UserGuideSoftConfigPageInfo<List<UserguideSoftConfig>>>>(NetWorkActivity.this) {
      @Override
      public void onError(ExceptionHandle.ResponseException exception) {
        myToast.showToast(NetWorkActivity.this,exception.getMessage());
        Log.d("carhandbook",exception.getMessage());
      }

      @Override
      public void onNext(UserGuideSoftConfigRForm<UserGuideSoftConfigPageInfo<List<UserguideSoftConfig>>> Response) {
        data=Response.getData().getList();
        code=Response.getCode();
        myToast.showToast(NetWorkActivity.this,code);
        generateAdapter.setData(data);
        generateAdapter.notifyDataSetChanged();
      }
    });

Well, the whole idea of exception handling for network requests is roughly like this.


Related articles: