SoFunction
Updated on 2025-03-11

Detailed explanation of the process of implementing custom Converter parsing interface using Retrofit

I don't know if the data you return when using Retrofit to access the background interface is the same format. For example, when logging in to the interface, the data returned in the background is different when we enter the password successfully or incorrectly, so we are addingGsonConverterFactoryWhen parsing background data, the background will return two different data, so it will causeGsonThe error message that failed to parse. Here we use the login interface of our project as an example to record your own solution.

Two data formats for successful and failed login:

{"success":false,"code":-1,"msg":"Error password","data":"Error password"}
{"success":true,"code":1,"msg":"The operation is successful","data":{"JXHDAPIToken":"1ccf7b01ed544882aacda365c8f620d2"}}

From the above data, we can find that the data returned in the background is different only in data, and the other fields are the same. At this time, we can extract several identical fields and modify themGsonConverterFactoryto parse different data.

Modify GsonConverterFactory:

We just need to click to enterGsonConverterFactoryLook at its code in the source code, we only need to modify the returned oneGsonResponseBodyConverterObject.

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

So let's click to enterGsonResponseBodyConverterCheck its code structure:

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
  private final Gson gson;
  private final TypeAdapter<T> adapter;
  GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
     = gson;
     = adapter;
  }
  @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = (());
    try {
      return (jsonReader);
    } finally {
      ();
    }
  }
}

We can't be directlyGsonResponseBodyConverterIt is modified directly, so we can customize oneMyGsonResponseBodyConverterkind:

final class MyGsonResponseBodyConverter<T> implements Converter<ResponseBody,T>{
    private Gson gson;
    private Type type;
    public MyGsonResponseBodyConverter(Gson gson, Type type) {
         = gson;
         = type;
    }
    @Override
    public T convert(ResponseBody value) throws IOException {
        String response = ();
        try {
            BaseBean baseBean = (response,);
            if (!()) {
                throw new DataResultException((),());
            }
            return ((response),type);
        }finally {
            ();
        }
    }
}

We extracted it from ourselvesBaseBeanTo determine the fields returned by the current interfacesuccess, iftrueIn the case offalseWe customize the situationDataResultExceptionThe exception class throws it.

public class BaseBean {
    private boolean success;
    private int code;
    private String msg;
    public boolean isSuccess() {
        return success;
    }
    public void setSuccess(boolean success) {
         = success;
    }
    public int getCode() {
        return code;
    }
    public void setCode(int code) {
         = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
         = msg;
    }
}

passDataResultExceptionGetsuccessforfalseThe data returned in the case:

public class DataResultException extends IOException {
    private String msg;
    private int code;
    public DataResultException(String msg, int code) {
         = msg;
         = code;
    }
    public DataResultException(String message, String msg, int code) {
        super(message);
         = msg;
         = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
         = msg;
    }
    public int getCode() {
        return code;
    }
    public void setCode(int code) {
         = code;
    }
}

At this time we areGsonConverterFactoryThe modification is basically completed, but we still need to customize it when we want to use itMyGsonConverterFactoryandMyGsonRequestBodyConverterTwo categories.

public class MyGsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
    private static final MediaType MEDIA_TYPE = ("application/json; charset=UTF-8");
    private static final Charset UTF_8 = ("UTF-8");
    private final Gson gson;
    private final TypeAdapter<T> adapter;
    MyGsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
         = gson;
         = adapter;
    }
    @Override public RequestBody convert(T value) throws IOException {
        Buffer buffer = new Buffer();
        Writer writer = new OutputStreamWriter((), UTF_8);
        JsonWriter jsonWriter = (writer);
        (jsonWriter, value);
        ();
        return (MEDIA_TYPE, ());
    }

This is mainly for customizing itMyGsonResponseBodyConverterAdd it, andMyGsonRequestBodyConverterThis class andGsonRequestBodyConverterThe source code content is the same, we just copy the content directly.

public final class MyGsonConverterFactory extends  {
    public static MyGsonConverterFactory create() {
        return create(new Gson());
    }
    public static MyGsonConverterFactory create(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        return new MyGsonConverterFactory(gson);
    }
    private final Gson gson;
    private MyGsonConverterFactory(Gson gson) {
         = gson;
    }
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
                                                            Retrofit retrofit) {
        return new MyGsonResponseBodyConverter<>(gson,type);
    }
    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type,
                                                          Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = ((type));
        return new MyGsonRequestBodyConverter<>(gson, adapter);
    }
}

We just need toMyGsonConverterFactoryReplace the previous oneGsonConverterFactoryJust:addConverterFactory(())

At this time, we will call the previous login interface. When our login fails, we will enteronErrorIn the method, at this time we can determine whether the exception is thrown. If so, we can obtain the current one.msgandcodeValue to perform some operations:

().getApi().login()
                .subscribeOn(())
                .observeOn(())
                .subscribe(new Subscriber<LoginResult>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                    }
                    @Override
                    public void onNext(LoginResult loginResult) {
                    }
                    @Override
                    public void onError(Throwable t) {
                     if (t instanceof DataResultException) {
                         DataResultException resultException = (DataResultException) t;
                         (TAG, "Code: " + () + "Message:" + ());
                       }
                    }
                    @Override
                    public void onComplete() {
                    }
                });

Here we canSubscriberIt is very complicated to encapsulate it, otherwise we need to make judgments every time we request:

public abstract class MySubscriber&lt;T&gt; implements Subscriber&lt;T&gt; {
    @Override
    public void onSubscribe(Subscription s) {
        (Long.MAX_VALUE);
    }
    @Override
    public void onComplete() {
    }
    @Override
    public void onError(Throwable t) {
        //This is done according to your project needs, such as code judgment.        if (t instanceof DataResultException) {
            DataResultException resultException = (DataResultException) t;
            (TAG, "Code: " + () + "Message:" + ());
        }
    }

We will only call after encapsulationonNextOf course, if we need other operations, we can also rewrite several other methods:

().getApi().login()
                .subscribeOn(())
                .observeOn(())
                .subscribe(new MySubscriber&lt;LoginResult&gt;() {
                    @Override
                    public void onNext(LoginResult loginResult) {
                       //Login successfully                    }
                });

Okay, it's over here. Before I knew it, I felt that time passed so quickly. I always felt that I didn't have enough time. I felt that there were still many things that I needed to learn by myself, but I was always inexplicably lazy. Haha, I hope I can continue to work hard to grow slowly on the road ahead.

This is the article about the detailed explanation of the process of implementing custom Converter parsing interfaces for Android using Retrofit. For more relevant content on Android custom Converter parsing interfaces, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!