Retrofit统一处理服务器返回参数

想了下还是写下这篇博客,去年遇到的一个坑。
关于这个服务器返回的请求状态,我以前用的是这种方式:
给 Android 开发者的 RxJava 详解
RxJava+Retrofit,在联网返回后如何先进行统一的判断?

这种方式对服务器返回的Json格式有要求,必须是这种样子的:

{
  "status": 1,
  "msg": "message",
  "data":{}
}

然后用这样的一个类去接收

public class Result<T> {
  public int status;
  public String msg;
  public T data;
}

我也觉得以上的方式很好用,直到我遇到了这种Json:

{
  "status": 1,
  "msg": "message",
  "data":{},
  "otherData":{},
  "adData":{}
}

上面给出的链接的方式是指定一个泛型作为接收data的实体对象,但是这里有三种data实体…… 我感觉我踩坑了

一、使用自定义ConvertAdapter

我又搜索了好久,看到了另一种方式,那就是Retrofit可以使用自定义的Converter,它的作用是可以将接受到的json转换成实体返回给我们。

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(ApiService.API_BASE_URL)
        .client(okHttpClient)
        .addConverterFactory(GsonConverterFactory.create()) // 就是这里
        .addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))
        .build(); 

一般情况下我们都使用GsonConverterFactory,当请求到json后,Retrofit就会调用GsonConverter将json转成我们需要的实体。

GsonConverterFactory一共只有三个类,并且代码量很少,如下:

public final class GsonConverterFactory extends Converter.Factory {
  /**
   * Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
   * decoding from JSON (when no charset is specified by a header) will use UTF-8.
   */
  public static GsonConverterFactory create() {
    return create(new Gson());
  }

  /**
   * Create an instance using {@code gson} for conversion. Encoding to JSON and
   * decoding from JSON (when no charset is specified by a header) will use UTF-8.
   */
  public static GsonConverterFactory create(Gson gson) {
    return new GsonConverterFactory(gson);
  }

  private final Gson gson;

  private GsonConverterFactory(Gson gson) {
    if (gson == null) throw new NullPointerException("gson == null");
    this.gson = gson;
  }

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

  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type,
      Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonRequestBodyConverter<>(gson, adapter);
  }
}
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
  private final Gson gson;
  private final TypeAdapter<T> adapter;

  GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
  }

  @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      return adapter.read(jsonReader);
    } finally {
      value.close();
    }
  }
}
final class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
  private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
  private static final Charset UTF_8 = Charset.forName("UTF-8");

  private final Gson gson;
  private final TypeAdapter<T> adapter;

  GsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
  }

  @Override public RequestBody convert(T value) throws IOException {
    Buffer buffer = new Buffer();
    Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
    JsonWriter jsonWriter = gson.newJsonWriter(writer);
    adapter.write(jsonWriter, value);
    jsonWriter.close();
    return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
  }
}

当我们发出一个POST JSON请求(直接用Retrofit post一个实体给服务器,请求的时候会自动将我们的实体转成Json)的时候,Retrofit就会调用GsonRequestBodyConverter的Convert方法将我们的实体转换成json。
而接受服务器返回的数据时,Retrofit会调用GsonResponseBodyConverter将Json数据转换成我们需要的实体。

既然如此,我们可以在Converter解析json的时候就做服务器参数的统一处理

我是将GsonConverterFactory的三个类拷贝出来修改了一下:
GsonConverterFactory和RequestBodyConverter几乎没有任何修改,我们需要更改类是GsonResponseBodyConverter,因为它才是将服务器的数据转换成实体了,我们在转换的过程中做统一处理。

/**
 * Created by AItsuki on 2016/12/16.
 */
final class ResponseBodyConverter<T> implements Converter<ResponseBody, T> {

    private static final int FAILURE = 0;       // 失败 提示失败msg
    private static final int SUCCESS = 1;       // 成功
    private static final int TOKEN_EXPIRE = 2;  // token过期
    private static final int SERVER_EXCEPTION = 3;  // 服务器异常

    private final Gson gson;
    private final TypeAdapter<T> adapter;

    ResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
        this.gson = gson;
        this.adapter = adapter;
    }

    @Override
    public T convert(ResponseBody value) throws IOException {
        String json = value.string();
        try {
            verify(json);
            return adapter.read(gson.newJsonReader(new StringReader(json)));
        } finally {
            value.close();
        }
    }

    private void verify(String json) {
        Result result = gson.fromJson(json, Result.class);
        if (result.state != SUCCESS) {
            switch (result.state) {
                case FAILURE:
                case SERVER_EXCEPTION:
                    throw new ApiException(result.msg);
                case TOKEN_EXPIRE:
                    throw new TokenExpireException(result.msg);
                default:
                    throw new UndefinedStateException();
            }
        }
    }
}
public class Result {
    public String msg;
    public int state;
}

将json解析成实体之前,我们先手动解析state和msg两个字段,根据状态抛出自定义异常即可。
但是我们的用于接收实体的对象也比较丑,一个类套着一个内部类!例如:

public class User {
    public Data data;

    public static class Data {
      public String username;
      public String phone;
      public String address;
    }
}

这也是一个蛋疼的地方,但是我没有更好的解决方式。
而处理自定义异常和上面给出的两个链接的方式也没什么不同,自定义一个Subscriber在OnError中统一接收处理即可。

二、建议

这是一个解决方案,但是将统一处理改成这种方式之前,我更推荐去找写接口文档或者服务器的哥们谈谈……
别给我们返回这种奇葩的Json:

{
  "status": 1,
  "msg": "message",
  "data":{},
  "otherData":{},
  "adData":{}
}

或者这种更加奇葩的:

{
    "reason": "成功的返回",
    "result": {
        "stat": "1",
        "data": {}
    }
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页