Retrofit2+Rxjava2 自定义GsonConvert 将Http返回Code状态统一处理

Retrofit响应数据及异常处理策略

这篇文章有提到自定义GsonConvert来统一解析外层包裹的HttpResult关于自定义Code的统一处理,让业务只关心业务实体。

但是这个封装的还不够彻底,业务调用以及Retrofit的定义中还是透明的知道HttpResult的存在。业务调用和Retrofit的API定义应该只关心具体的业务Entity。HttpResult应该是在Gson解析的过程中自动处理,业务处不需要关心和知道它的存在。

那么问题来了,怎么给Entity包裹上HttpResult,变成HttpResult<Entity>勒。通过断点查看,发现HttpResult<Entity>的结构在GsonConvert的responseBodyConverter方法中的type 的实际类型是ParameterizedTypeImpl类型的,如下:

image.png

然后全局搜索ParameterizedTypeImpl 发现

image.png

于是拷贝retrofit2中的Utils类,简单的走读了一下源码,尽管没太看懂,然后尝试调用api,最后成功的根据Entity和HttpResult,构造出了HttpResult<Entity>的ParameterizedTypeImpl对象

    //给Entity 包裹到HttpResult 的泛型里,成为HttpResult<Entity>
   Utils.ParameterizedTypeImpl parameterizedType =
        new Utils.ParameterizedTypeImpl(null, HttpResult.class, typeToken.getType());

拷贝Retrofit2的GsonConverterFactory与GsonRequestBodyConverter、GsonResponseBodyConverter

修改MyGsonConverterFactory的responseBodyConverter方法的传参

public final class MyGsonConverterFactory<T> extends Factory {
  private final Gson gson;
  public static MyGsonConverterFactory create() {
    return create(new Gson());
  }

  public static MyGsonConverterFactory create(Gson gson) {
    if(gson == null) {
      throw new NullPointerException("gson == null");
    } else {
      return new MyGsonConverterFactory(gson);
    }
  }

  private MyGsonConverterFactory(Gson gson) {
    this.gson = gson;
  }

  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
    return new GsonResponseBodyConverter(this.gson,TypeToken.get(type));
  }

  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    TypeAdapter<?> adapter = this.gson.getAdapter(TypeToken.get(type));
    return new GsonRequestBodyConverter(this.gson, adapter);
  }
}

修改GsonResponseBodyConverter的构造入参,和泛型定义

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
  private final Gson gson;
  private final TypeAdapter<HttpResult<T>> adapter;
  GsonResponseBodyConverter(Gson gson, TypeToken<T> typeToken) {
    this.gson = gson;
    //给Entity 包裹到HttpResult 的泛型里,成为HttpResult<Entity>
   Utils.ParameterizedTypeImpl parameterizedType =
        new Utils.ParameterizedTypeImpl(null, HttpResult.class, typeToken.getType());
    this.adapter =
        (TypeAdapter<HttpResult<T>>) this.gson.getAdapter(TypeToken.get(parameterizedType));
  }


  @Override
  public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = this.gson.newJsonReader(value.charStream());

    HttpResult<T> result;
    try {
      result = this.adapter.read(jsonReader);
      //code!=1,服务器校验返回的错误信息
      if (result != null&&result.getCode()!=1){
            throw new NetApiException(result.getCode(),result.getMsg());
        }
    } finally {
      value.close();
    }
    //返回实际的Entity
    return result == null ? null : result.getData();
  }
}

这样在定义的时候和使用的时候都只知道业务的Entity,而不知道HttpResult的存在。

如:

API定义

public interface LoginApi {

  @FormUrlEncoded @POST("lawyer/login") Flowable<UserEntity> login(
      @Field("user") String phoneNum, @Field("pwd") String password);
}

调用,code异常在异常中判断NetApiException

    loginBiz.login(userName, pw)
        .compose(RxTransformers.<UserEntity>io_main())
        .compose(RxTransformers.waitLoadingTransformer(loginView))
        .subscribe(new Consumer<UserEntity>() {
          @Override public void accept(UserEntity userEntity) throws Exception {
            Log.i(TAG, "accept: userEntity=" + userEntity.getUserName());
          }
        }, new Consumer<Throwable>() {
          @Override public void accept(Throwable throwable) throws Exception {
            if (throwable instanceof NetApiException) {
              Log.i(TAG, "accept:登录信息错误 " + throwable.getMessage());
            } else {
              Log.i(TAG, "accept: " + throwable.getMessage());
            }
          }
        });

附上相关类的定义

public class HttpResult<T> {
  private int code;
  private String msg;
  private T data;

  public int getCode() {
    return code;
  }

  public String getMsg() {
    return msg;
  }

  public T getData() {
    return data;
  }
}

public class NetApiException extends RuntimeException{
  private int code;
  private String msg;

  public NetApiException(int code, String msg) {
    this.code = code;
    this.msg = msg;
  }

  @Override public String toString() {
    return "Code="+code+"   Msg="+msg;
  }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容