安卓使用retrofit统一处理服务器请求错误

正常情况下我们开发app服务器返回的json一般是这样标准的形式。

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

所以我们可以在客户端定义这样的格式来进行统一处理。

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

最近遇到了这样的问题,服务器返回的data有时候是对象,有时候又是数组,还有可能是字符串,这样使用第三方解析就比较麻烦了。但是需要处理返回code不为0的情况,比如给用户一些提示什么的。所以写下来作为记录,防止以后再遇到。遇到这种坑,要么和后台协商返回统一格式的json字符串,如果后台不改,我们只能自己解决了。

安卓使用retrofit统一处理服务器请求错误_第1张图片

一、使用自定义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 MyGsonConverterFactory 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 MyGsonConverterFactory 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 MyGsonConverterFactory create(Gson gson) {
        return new MyGsonConverterFactory(gson);
    }

    private final Gson gson;

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

    @Override
    public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        return new MyGsonResponseBodyConverter<>(gson, type);
    }

    @Override
    public Converter requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        TypeAdapter adapter = gson.getAdapter(TypeToken.get(type));
        return new MyGsonRequestBodyConverter<>(gson, adapter);
    }
}
final class MyGsonRequestBodyConverter implements Converter {
    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 adapter;

    MyGsonRequestBodyConverter(Gson gson, TypeAdapter 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());
    }
}

主要操作在这个类进行

public class MyGsonResponseBodyConverter implements Converter {

    private final Gson gson;
    private final Type type;

    MyGsonResponseBodyConverter(Gson gson, Type type) {
        this.gson = gson;
        this.type = type;
        apiHandle = new ApiHandle();
    }


    @Override
    public T convert(ResponseBody value) throws IOException {
        String response = value.string();
        try {
            return gson.fromJson(response, type);
        } finally {
            value.close();
        }
    }

当我们发出一个POST JSON请求(直接用Retrofit post一个实体给服务器,请求的时候会自动将我们的实体转成Json)的时候,Retrofit就会调用GsonRequestBodyConverter的Convert方法将我们的实体转换成json。而接受服务器返回的数据时,Retrofit会调用GsonResponseBodyConverter将Json数据转换成我们需要的实体。
既然如此,我们可以在Converter解析json的时候就做服务器参数的统一处理
我是将GsonConverterFactory的三个类拷贝出来修改了一下:GsonConverterFactory和RequestBodyConverter几乎没有任何修改,我们需要更改类是GsonResponseBodyConverter,因为它才是将服务器的数据转换成实体了,我们在转换的过程中做统一处理。
这里需要说明的是因为后台返回的data时刻会改变,所以我们需要解决这个问题,我是这么处理的,首先自己定义一个类去存放code和message

public class BaseBean {
    private int code;
    private String msg;
    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

这样就能避免我们在解析data的时候出现解析错误,这里我的处理是在MyGsonResponseBodyConverter里面的convert方法拦截response

  public T convert(ResponseBody value) throws IOException {
        String response = value.string();
        try {
            BaseBean baseBean = gson.fromJson(response, BaseBean.class);//拦截code非0情况
            if (baseBean.getCode() != 0) {
                new Thread() {
                    @Override
                    public void run() {
                        Message msg = new Message();
                        msg.what = 1;
                        msg.obj = baseBean.getMsg();
                        handler.sendMessage(msg);
                    }
                }.start();
            }
            return gson.fromJson(response, type);
        } finally {
            value.close();
        }
    }

这里我们判断code非0就是请求没有达到我们定义的预期效果,我这里是简单处理给了个吐司,也可以定义接口回调处理,但是因为是子线程所以要用handler更新ui。
一般情况下如果data格式固定可以在这里自定义异常处理,因为我是data不固定,只能先取cade和message简单处理。最好还是和后台小哥协商好最好了。最好不要遇到这种坑~~~

你可能感兴趣的:(安卓使用retrofit统一处理服务器请求错误)