The previous two articles introduced Retrofit+RxJava + OkHttp in the actual project, divided into the basic and encapsulation, in the actual project network request transformation, also encountered some problems, here to share with you. The following text:

One tag that you might be particularly interested in when you’re using Retrofit is the @Body tag, which is very handy because when you’re asking for too many parameters, you don’t have to write a bunch of @field tags to pass parameters, and you don’t have to dump parameters into a map to use the @FieldMap tag to pass parameters. We just define an entity, @body tag, and the argument is the entity that we define. Is it very convenient? The answer is yes, but there are some pitfalls:

Error 1: @body parameters cannot be used with form or multi-part encoding.

The diagram below:




1.png

The error message is clear: @body tag cannot be used at the same time as @Formurlencoded and @multipart tags.

One of the things that maybe some of you who started out with Retrofit misunderstood was that inside the @body tag, you might have taken every attribute out and passed it to the server as a form. (That’s what I thought at first, but looking at the source code, That’s not true), just like the @field tag and the @fieldMap tag, so when you use the @body tag, it says:

@FormUrlEncoded
@POST("/api/getUser")
Observable getUserInfo(@Body User user);Copy the code

I’ve seen several articles about Retrofit written like this, and they probably didn’t test it at all, like the code examples above would throw the exceptions mentioned above, so @formurlencoded would just do it.

Source code trace: we can take a look at the source code for this error, see how to determine the source code, parse the tag in the ServiceMethod class. The code for throwing an exception is as follows:

if (isFormEncoded || isMultipart) {  
            throw parameterError(p,  "@Body parameters cannot be used with form or multi-part encoding.");
}Copy the code

IsFormEncoded to true or isMultipart to true is used to throw an exception. The names of the variables show that the @formurlencoded or @multipart tags are used, so let’s see where the two variables are set to true:

. Else if (annotation instanceof FormUrlEncoded) {if (isMultipart) {throw methodError("Only one encoding annotation is allowed."); } isFormEncoded = true; } else if (annotation instanceof Multipart) { if (isFormEncoded) { throw methodError("Only one encoding annotation is allowed."); } isMultipart = true; }Copy the code

As can be seen from the source code, the @formurlencoded isFormEncoded tag is set to true, and the @multipart tag is set to true. So now we know why @body can’t be used with @Formurlencoded and Multipart tags at the same time.

Unable to create @body Converter for XXXEntry

The diagram below:




3.png

Error: Cannot create converter for corresponding entity. Of course I am, If you haven’t seen Retrofit+RxJava + OkHttp, please go to Retrofit+RxJava + OkHttp to make web requests easy. What does this Converter have to do with our error here, which brings us back to the question I asked above, in what form is the @body tag uploaded as an argument? The answer is the Json string of the @body parameter entity uploaded, so you need an internal GsonCoverter to convert the entity into a Json string, That is, when the package in the Manager unified configuration addConverterFactory (GsonConverterFactory. The create ()), then why complains here? The reason is: For the project, we did some encryption and decryption of the request parameters and the request results, so Retrofit’s default GsonConverterFactory didn’t meet our requirements, so we rewrote a GsonConverterFactory ourselves. The responseBodyConverter is a responseBodyConverter that returns the result of the request, and the requestBodyConverter that returns a request parameter. I only wrote the first method in the project, so the rest of the API’s interfaces are correct, and the ones that use the @body tag will give an error. Even the source of the interface is as follows:

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

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

I was also this error pit for a long time (bitter smile 😂😂), read the source code to find the error (so see the source code is very important ah!! , then let’s take a look at the source code, the first place to throw exceptions:

try {  
converter = retrofit.requestBodyConverter(type, annotations, methodAnnotations);
} catch (RuntimeException e) { 
 // Wide exception range because factories are user code. 
 throw parameterError(e, p, "Unable to create @Body converter for %s", type);
}Copy the code

This is then called into Retrofit’s nextRequestBodyConverter:

int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {  
Converter.Factory factory = converterFactories.get(i);  
Converter converter =      factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, this); 
 if (converter != null) {
    //noinspection unchecked   
 return (Converter) converter; 
 }
}Copy the code

See yet? The above code is invoked factory. RequestBodyConverter (type, parameterAnnotations methodAnnotations, this), And this factory is our through addConverterFactory (GsonConverterFactory. The create ()) in the configuration to Retrofit the factory. Ok, now we also know why this error occurred from the source code. Finally, let’s take a look at what happens when the @body tag is passed to the server.




2.png

That is, the entity is converted to a JSON string and passed to the server

At the end

These are some of the problems I’ve encountered with Retrofit and some of the ideas I’ve come up with to solve them. When we have a problem, sometimes the source code is the most direct and effective way to do it. Even through Baidu, Google found the answer, also want to look at the source code, see why the error, know why, also want to know why.