Simple to use

Add the dependent
Implementation 'com. Squareup. Retrofit2: retrofit: 2.9.0' implementation 'com. Squareup. Retrofit2: converter - gson: 2.9.0'Copy the code
Create an interface and return an object
interface GitHubService { @GET("users/{user}/repos") fun listRepos(@Path("user") user: String?) : Call<List<Repo>> }Copy the code
data class Repo(
    val allow_forking: Boolean,
    ...
    val watchers_count: Int
)

data class Owner(
    val avatar_url: String,
    ...
    val url: String
)
Copy the code
Called in the activity
val retrofit = Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(GsonConverterFactory.create()) .build() val service = retrofit.create(GitHubService::class.java) val listRepos = service.listRepos("octocat") listRepos.enqueue(object :Callback<List<Repo>> { override fun onResponse(call: Call<List<Repo>>, response: Response<List<Repo>>) { println("response: ${response.body()!! [0].name}") } override fun onFailure(call: Call<List<Repo>>, t: Throwable) { } })Copy the code

The source code interpretation

Analyze from the last enqueue method executed: If you click on the enqueue() method to enter a call interface class, you don’t know its implementation. Look up the service.listrepos (“octocat”) method and find that listRepos is also a method in the interface. Continue to look for retrofit upgrade. The create (GitHubService: : class. Java), view the Crete method

public <T> T create(final Class<T> service) { validateServiceInterface(service); return (T) Proxy.newProxyInstance( service.getClassLoader(), new Class<? >[] {service}, new InvocationHandler() { private final Platform platform = Platform.get(); private final Object[] emptyArgs = new Object[0]; @Override public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { if (method.getDeclaringClass() == Object.class) { return method.invoke(this,  args); } args = args ! = null ? args : emptyArgs; return platform.isDefaultMethod(method) ? platform.invokeDefaultMethod(method, service, proxy, args) : loadServiceMethod(method).invoke(args); }}); }Copy the code

Enter the validateServiceInterface method

private void validateServiceInterface(Class<? > service) {// Check whether the interface if (! service.isInterface()) { throw new IllegalArgumentException("API declarations must be interfaces."); } Deque<Class<? >> check = new ArrayDeque<>(1); // Add the interface to the queue check.add(service); while (! check.isEmpty()) { Class<? > candidate = check.removeFirst(); / / getTypeParameters () refers to the generic interface on the if (candidate. GetTypeParameters (). The length! = 0) { StringBuilder message = new StringBuilder("Type parameters are unsupported on ").append(candidate.getName()); if (candidate ! = service) { message.append(" which is an interface of ").append(service.getName()); } throw new IllegalArgumentException(message.toString()); } // Add the parent's interface to the collections.addall (check, candidate.getinterfaces ()); } // If (validation) {Platform Platform = platform.get (); for (Method method : service.getDeclaredMethods()) { if (! platform.isDefaultMethod(method) && ! Modifier.isStatic(method.getModifiers())) { loadServiceMethod(method); }}}}Copy the code

Service is GitHubService: : class. Java

ValidateServiceInterface method mainly do some validation, figure out whether the service interface, is there a generic, whether analytic judgment in advance, etc.

If the validategbit/s flag is true, it enters the method ahead of time to validate the service and parse it.

. Analysis the Proxy newProxyInstance ()

(T) Proxy.newProxyInstance( service.getClassLoader(), new Class<? >[] {service}, new InvocationHandler() { private final Platform platform = Platform.get(); private final Object[] emptyArgs = new Object[0]; @Override public @Nullable Object invoke(Object proxy, Method method, @nullable Object[] args) throws Throwable {// Is an Object method, Class == object.class) {return method.invoke(this, args); } args = args ! = null ? args : emptyArgs; Return platform.isDefaultMethod(method)? Return platform.isDefaultMethod(method)? platform.invokeDefaultMethod(method, service, proxy, args) : loadServiceMethod(method).invoke(args); }});Copy the code

The static method newProxyInstance() of the Proxy class returns an implementation-class object that implements the specified interface.

A dynamic proxy is a class that’s generated at run time, that implements a set of interfaces that you specify, that doesn’t have a.java file, that’s generated at run time, you don’t care what type it is, you just need to know what interface it implements

The Proxy class newInstance() method takes three arguments

  • Loader: Which class loader is used to load the proxy object
  • Interfaces: Interfaces that a dynamic proxy class needs to implement, which can be multiple interfaces
  • H: When a dynamic proxy method is executed, the invoke method in H is invoked to execute it

The three arguments to the Invoke method inside the InvocationHandler

  • Proxy: is the proxy object returned by the newProxyInstance method
  • Method: indicates the method to be invoked
  • Args: parameters in a method

Go to the Invoke method and find a method in the abstract class ServiceMethod. Look up and enter the loadServiceMethod method

ServiceMethod<? > loadServiceMethod(Method Method) {// Fetch ServiceMethod<? > result = serviceMethodCache.get(method); if (result ! = null) return result; synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); If (result = = null) {/ / create a new ServiceMethod into the cache result = ServiceMethod. ParseAnnotations (this, method); serviceMethodCache.put(method, result); } } return result; }Copy the code

Enter ServiceMethod. ParseAnnotations (this, method) method

static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
  RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

  Type returnType = method.getGenericReturnType();
  if (Utils.hasUnresolvableType(returnType)) {
    throw methodError(
        method,
        "Method return type must not include a type variable or wildcard: %s",
        returnType);
  }
  if (returnType == void.class) {
    throw methodError(method, "Service methods cannot return void.");
  }

  return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
Copy the code

Go into the parseAnnotations method

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) { boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction; boolean continuationWantsResponse = false; boolean continuationBodyNullable = false; Annotation[] annotations = method.getAnnotations(); Type adapterType; if (isKotlinSuspendFunction) { Type[] parameterTypes = method.getGenericParameterTypes(); Type responseType = Utils.getParameterLowerBound( 0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]); if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) { responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType); continuationWantsResponse = true; } else { } adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType); annotations = SkipCallbackExecutorImpl.ensurePresent(annotations); } else { adapterType = method.getGenericReturnType(); } CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, adapterType, annotations); Type responseType = callAdapter.responseType(); if (responseType == okhttp3.Response.class) { throw methodError( method, "'" + getRawType(responseType).getName() + "' is not a valid response body type. Did you mean ResponseBody?" ); } if (responseType == Response.class) { throw methodError(method, "Response must include generic type (e.g., Response<String>)"); } if (requestFactory.httpMethod.equals("HEAD") && ! Void.class.equals(responseType)) { throw methodError(method, "HEAD method must use Void as response type."); } Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType); okhttp3.Call.Factory callFactory = retrofit.callFactory; if (! isKotlinSuspendFunction) { return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); } else if (continuationWantsResponse) { //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object. return  (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForResponse<>( requestFactory, callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter); } else { //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object. return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForBody<>( requestFactory, callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter, continuationBodyNullable); }}Copy the code

You find that this class inherits from the ServiceMethod class and also implements the invoke method of the class

HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT>
Copy the code

So let’s examine the Invoke method first

@Override
final @Nullable ReturnT invoke(Object[] args) {
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
  return adapt(call, args);
}

protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);
Copy the code

You find that you end up calling the class’s abstract method adapt

So let’s go into the parseAnnotations method again

if (! isKotlinSuspendFunction) { return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); } else if (continuationWantsResponse) { return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForResponse<>( requestFactory, callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter); } else { return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForBody<>( requestFactory, callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter, continuationBodyNullable); }Copy the code

New CallStuck <>(requestFactory, callFactory, responseConverter, callAdapter

static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> { private final CallAdapter<ResponseT, ReturnT> callAdapter; CallAdapted( RequestFactory requestFactory, okhttp3.Call.Factory callFactory, Converter<ResponseBody, ResponseT> responseConverter, CallAdapter<ResponseT, ReturnT> callAdapter) { super(requestFactory, callFactory, responseConverter); this.callAdapter = callAdapter; } @Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) { return callAdapter.adapt(call); }}Copy the code

The callAdapter.adapt(Call) discovery is a method inside the callAdapter interface.

Call enQueue () is also a Call interface

public void enqueue(final Callback<T> callback) { Objects.requireNonNull(callback, "callback == null"); okhttp3.Call call; Throwable failure; synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; call = rawCall; failure = creationFailure; If (call == null && failure == null) {try {// Create a call call = rawCall = createRawCall(); } catch (Throwable t) { throwIfFatal(t); failure = creationFailure = t; } } } if (failure ! = null) { callback.onFailure(this, failure); return; } if (canceled) { call.cancel(); Callback(new okhttp3.Callback() {@override public void onResponse(okhttp3. call call, okhttp3.Response rawResponse) { Response<T> response; RawResponse is an okHTTP object. Response is retrofit's response = parseResponse(rawResponse); } catch (Throwable e) { throwIfFatal(e); callFailure(e); return; } try { callback.onResponse(OkHttpCall.this, response); } catch (Throwable t) { throwIfFatal(t); t.printStackTrace(); // TODO this is not great } } @Override public void onFailure(okhttp3.Call call, IOException e) { callFailure(e); } private void callFailure(Throwable e) { try { callback.onFailure(OkHttpCall.this, e); } catch (Throwable t) { throwIfFatal(t); t.printStackTrace(); // TODO this is not great } } }); }Copy the code

Go into the createRawCall method and create a call (a call from OKHttp3 is not retrofit)


private okhttp3.Call createRawCall() throws IOException {
  okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
  if (call == null) {
    throw new NullPointerException("Call.Factory returned null.");
  }
  return call;
}
Copy the code

Finally, network requests are made through okHTTP’s enqueue method.

The general process looks like this, but there is one Calladapter that has not been completed. Going back to parseAnnotations, I find that callAdapters are created using the createCallAdapter method

CallAdapter<ResponseT, ReturnT> callAdapter =
    createCallAdapter(retrofit, method, adapterType, annotations);
Copy the code

Enter the createCallAdapter method

private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter( Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) { try { return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw methodError(method, e, "Unable to create call adapter for %s", returnType); }}Copy the code

Continue into the callAdapter method

public CallAdapter<? ,? > callAdapter(Type returnType, Annotation[] annotations) { return nextCallAdapter(null, returnType, annotations); }Copy the code

Continue into the nextCallAdapter

public CallAdapter<? ,? > nextCallAdapter( @Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) { Objects.requireNonNull(returnType, "returnType == null"); Objects.requireNonNull(annotations, "annotations == null"); int start = callAdapterFactories.indexOf(skipPast) + 1; for (int i = start, count = callAdapterFactories.size(); i < count; i++) { CallAdapter<? ,? > adapter = callAdapterFactories.get(i).get(returnType, annotations, this); if (adapter ! = null) { return adapter; } } StringBuilder builder = new StringBuilder("Could not locate call adapter for ").append(returnType).append(".\n"); if (skipPast ! = null) { builder.append(" Skipped:"); for (int i = 0; i < start; i++) { builder.append("\n * ").append(callAdapterFactories.get(i).getClass().getName()); } builder.append('\n'); } builder.append(" Tried:"); for (int i = start, count = callAdapterFactories.size(); i < count; i++) { builder.append("\n * ").append(callAdapterFactories.get(i).getClass().getName()); } throw new IllegalArgumentException(builder.toString()); }Copy the code

Go into the callAdapterFactories class and see when to initialize

Retrofit(
    okhttp3.Call.Factory callFactory,
    HttpUrl baseUrl,
    List<Converter.Factory> converterFactories,
    List<CallAdapter.Factory> callAdapterFactories,
    @Nullable Executor callbackExecutor,
    boolean validateEagerly) {
  this.callFactory = callFactory;
  this.baseUrl = baseUrl;
  this.converterFactories = converterFactories; // Copy+unmodifiable at call site.
  this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
  this.callbackExecutor = callbackExecutor;
  this.validateEagerly = validateEagerly;
}
Copy the code

Check out where Retrofit was created

  return new Retrofit(
      callFactory,
      baseUrl,
      unmodifiableList(converterFactories),
      unmodifiableList(callAdapterFactories),
      callbackExecutor,
      validateEagerly);
}
Copy the code

Find where the callAdapterFactories are assigned

// Make a defensive copy of the adapters and add the default Call adapter. List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories); / / add value to the factory callAdapterFactories. AddAll (platform. DefaultCallAdapterFactories (callbackExecutor));Copy the code

Enter the defaultCallAdapterFactories

List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
    @Nullable Executor callbackExecutor) {
  DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
  return hasJava8Types
      ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
      : singletonList(executorFactory);
}
Copy the code

Enter DefaultCallAdapterFactory for the get method

final class DefaultCallAdapterFactory extends CallAdapter.Factory { private final @Nullable Executor callbackExecutor; DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) { this.callbackExecutor = callbackExecutor; } @Override public @Nullable CallAdapter<? ,? > get( Type returnType, Annotation[] annotations, Retrofit retrofit) { if (getRawType(returnType) ! = Call.class) { return null; } if (! (returnType instanceof ParameterizedType)) { throw new IllegalArgumentException( "Call return type must be parameterized  as Call<Foo> or Call<? extends Foo>"); } final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType); final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class) ? null : callbackExecutor; return new CallAdapter<Object, Call<? >>() { @Override public Type responseType() { return responseType; } @Override public Call<Object> adapt(Call<Object> call) { return executor == null ? call : new ExecutorCallbackCall<>(executor, call); }}; }}Copy the code

Enter the ExecutorCallbackCall class

Static final class ExecutorCallbackCall<T> implements Call<T> {//Executor is only an interface, It is the foundation of the Java/Android thread pool framework final Executor callbackExecutor; final Call<T> delegate; ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) { this.callbackExecutor = callbackExecutor; this.delegate = delegate; } @Override public void enqueue(final Callback<T> callback) { Objects.requireNonNull(callback, "callback == null"); Call delegate.enqueue(new Callback<T>() {@override public void onResponse(call <T> call, Final Response<T> Response) {// Cancel the thread before callbackExecutor.execute(() -> {if (delegate.iscanceled ()) { callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled")); } else { callback.onResponse(ExecutorCallbackCall.this, response); }}); } @Override public void onFailure(Call<T> call, final Throwable t) { callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t)); }}); }}Copy the code

Look at the callbackExecutor

final Executor executor =
    Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
        ? null
        : callbackExecutor;
Copy the code

Find defaultCallAdapterFactories a incoming parameters

List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
    @Nullable Executor callbackExecutor) {
  DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
  return hasJava8Types
      ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
      : singletonList(executorFactory);
}
Copy the code

Back here

// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
Copy the code

Find the assignment

Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
  callbackExecutor = platform.defaultCallbackExecutor();
}
Copy the code

Enter the defaultCallbackExecutor

@Nullable
Executor defaultCallbackExecutor() {
  return null;
}
Copy the code

Discovery returns a NULL discovery Platform this class is a platform-dependent class

private static Platform findPlatform() { return "Dalvik".equals(System.getProperty("java.vm.name")) ? new Android() // :  new Platform(true); }Copy the code

So I found this method in Android

static final class Android extends Platform { Android() { super(Build.VERSION.SDK_INT >= 24); } @Override public Executor defaultCallbackExecutor() { return new MainThreadExecutor(); } @Nullable @Override Object invokeDefaultMethod( Method method, Class<? > declaringClass, Object object, Object... args) throws Throwable { if (Build.VERSION.SDK_INT < 26) { throw new UnsupportedOperationException( "Calling default methods on API 24 and 25 is not supported"); } return super.invokeDefaultMethod(method, declaringClass, object, args); } static final class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable r) { handler.post(r); }}}Copy the code

Enter MainThreadExecutor and suddenly discover that the switch is to switch the processing of the return value to the UI thread

static final class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable r) { handler.post(r); }}Copy the code
How is okhttp3. Call created
private okhttp3.Call createRawCall() throws IOException {
  okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
  if (call == null) {
    throw new NullPointerException("Call.Factory returned null.");
  }
  return call;
}
Copy the code

Enter parseAnnotations of the ServiceMethod class

static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
  RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

  Type returnType = method.getGenericReturnType();
  if (Utils.hasUnresolvableType(returnType)) {
    throw methodError(
        method,
        "Method return type must not include a type variable or wildcard: %s",
        returnType);
  }
  if (returnType == void.class) {
    throw methodError(method, "Service methods cannot return void.");
  }

  return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
Copy the code

Go into the parseAnnotations method

static RequestFactory parseAnnotations(Retrofit retrofit, Method method) { return new Builder(retrofit, method).build();  }Copy the code

Enter the build method

RequestFactory build() { for (Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation); } if (httpMethod == null) { throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.)."); } if (! hasBody) { if (isMultipart) { throw methodError( method, "Multipart can only be specified on HTTP methods with request body (e.g., @POST)."); } if (isFormEncoded) { throw methodError( method, "FormUrlEncoded can only be specified on HTTP methods with " + "request body (e.g., @POST)."); } } int parameterCount = parameterAnnotationsArray.length; parameterHandlers = new ParameterHandler<? >[parameterCount]; for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) { parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter); } if (relativeUrl == null && ! gotUrl) { throw methodError(method, "Missing either @%s URL or @Url parameter.", httpMethod); } if (! isFormEncoded && ! isMultipart && ! hasBody && gotBody) { throw methodError(method, "Non-body HTTP method cannot contain @Body."); } if (isFormEncoded && ! gotField) { throw methodError(method, "Form-encoded method must contain at least one @Field."); } if (isMultipart && ! gotPart) { throw methodError(method, "Multipart method must contain at least one @Part."); } return new RequestFactory(this); }Copy the code

Go to the parseMethodAnnotation method

private void parseMethodAnnotation(Annotation annotation) { if (annotation instanceof DELETE) { parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false); } else if (annotation instanceof GET) { parseHttpMethodAndPath("GET", ((GET) annotation).value(), false); } else if (annotation instanceof HEAD) { parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false); } else if (annotation instanceof PATCH) { parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true); } else if (annotation instanceof POST) { parseHttpMethodAndPath("POST", ((POST) annotation).value(), true); } else if (annotation instanceof PUT) { parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true); } else if (annotation instanceof OPTIONS) { parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false); } else if (annotation instanceof HTTP) { HTTP http = (HTTP) annotation; parseHttpMethodAndPath(http.method(), http.path(), http.hasBody()); } else if (annotation instanceof retrofit2.http.Headers) { String[] headersToParse = ((retrofit2.http.Headers) annotation).value(); if (headersToParse.length == 0) { throw methodError(method, "@Headers annotation is empty."); } headers = parseHeaders(headersToParse); } else if (annotation instanceof Multipart) { if (isFormEncoded) { throw methodError(method, "Only one encoding annotation is allowed."); } isMultipart = true; } else if (annotation instanceof FormUrlEncoded) { if (isMultipart) { throw methodError(method, "Only one encoding annotation is allowed."); } isFormEncoded = true; }}Copy the code

The parseHttpMethodAndPath method mainly records some behavior of the annotations

private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) { if (this.httpMethod ! = null) { throw methodError( method, "Only one HTTP method is allowed. Found: %s and %s.", this.httpMethod, httpMethod); } this.httpMethod = httpMethod; this.hasBody = hasBody; if (value.isEmpty()) { return; } // Get the relative URL path and existing query string, if present. int question = value.indexOf('? '); if (question ! = -1 && question < value.length() - 1) { // Ensure the query string does not have any named parameters. String queryParams = value.substring(question + 1); Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams); if (queryParamMatcher.find()) { throw methodError( method, "URL query string "%s" must not have replace block. " + "For dynamic query parameters use @Query.", queryParams); } } this.relativeUrl = value; this.relativeUrlParamNames = parsePathParameters(value); }Copy the code

ParseParameter This method parses parameters

private @Nullable ParameterHandler<? > parseParameter( int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) { ParameterHandler<? > result = null; if (annotations ! = null) { for (Annotation annotation : annotations) { ParameterHandler<? > annotationAction = parseParameterAnnotation(p, parameterType, annotations, annotation); if (annotationAction == null) { continue; } if (result ! = null) { throw parameterError( method, p, "Multiple Retrofit annotations found, only one allowed."); } result = annotationAction; } } if (result == null) { if (allowContinuation) { try { if (Utils.getRawType(parameterType) == Continuation.class) { isKotlinSuspendFunction = true; return null; } } catch (NoClassDefFoundError ignored) { } } throw parameterError(method, p, "No Retrofit annotation found."); } return result; }Copy the code

The parseParameterAnnotation method analyzes and records those annotation parameters on the interface

private ParameterHandler<?> parseParameterAnnotation(
    int p, Type type, Annotation[] annotations, Annotation annotation) {
  if (annotation instanceof Url) {
    validateResolvableType(p, type);
    if (gotUrl) {
      throw parameterError(method, p, "Multiple @Url method annotations found.");
    }
    if (gotPath) {
      throw parameterError(method, p, "@Path parameters may not be used with @Url.");
    }
    if (gotQuery) {
      throw parameterError(method, p, "A @Url parameter must not come after a @Query.");
    }
    if (gotQueryName) {
      throw parameterError(method, p, "A @Url parameter must not come after a @QueryName.");
    }
    if (gotQueryMap) {
      throw parameterError(method, p, "A @Url parameter must not come after a @QueryMap.");
    }
    if (relativeUrl != null) {
      throw parameterError(method, p, "@Url cannot be used with @%s URL", httpMethod);
    }

    gotUrl = true;

    if (type == HttpUrl.class
        || type == String.class
        || type == URI.class
        || (type instanceof Class && "android.net.Uri".equals(((Class<?>) type).getName()))) {
      return new ParameterHandler.RelativeUrl(method, p);
    } else {
      throw parameterError(
          method,
          p,
          "@Url must be okhttp3.HttpUrl, String, java.net.URI, or android.net.Uri type.");
    }

  } else if (annotation instanceof Path) {
    validateResolvableType(p, type);
    if (gotQuery) {
      throw parameterError(method, p, "A @Path parameter must not come after a @Query.");
    }
    if (gotQueryName) {
      throw parameterError(method, p, "A @Path parameter must not come after a @QueryName.");
    }
    if (gotQueryMap) {
      throw parameterError(method, p, "A @Path parameter must not come after a @QueryMap.");
    }
    if (gotUrl) {
      throw parameterError(method, p, "@Path parameters may not be used with @Url.");
    }
    if (relativeUrl == null) {
      throw parameterError(
          method, p, "@Path can only be used with relative url on @%s", httpMethod);
    }
    gotPath = true;

    Path path = (Path) annotation;
    String name = path.value();
    validatePathName(p, name);

    Converter<?, String> converter = retrofit.stringConverter(type, annotations);
    return new ParameterHandler.Path<>(method, p, name, converter, path.encoded());

  } else if (annotation instanceof Query) {
    validateResolvableType(p, type);
    Query query = (Query) annotation;
    String name = query.value();
    boolean encoded = query.encoded();

    Class<?> rawParameterType = Utils.getRawType(type);
    gotQuery = true;
    if (Iterable.class.isAssignableFrom(rawParameterType)) {
      if (!(type instanceof ParameterizedType)) {
        throw parameterError(
            method,
            p,
            rawParameterType.getSimpleName()
                + " must include generic type (e.g., "
                + rawParameterType.getSimpleName()
                + "<String>)");
      }
      ParameterizedType parameterizedType = (ParameterizedType) type;
      Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
      Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations);
      return new ParameterHandler.Query<>(name, converter, encoded).iterable();
    } else if (rawParameterType.isArray()) {
      Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
      Converter<?, String> converter =
          retrofit.stringConverter(arrayComponentType, annotations);
      return new ParameterHandler.Query<>(name, converter, encoded).array();
    } else {
      Converter<?, String> converter = retrofit.stringConverter(type, annotations);
      return new ParameterHandler.Query<>(name, converter, encoded);
    }

  } else if (annotation instanceof QueryName) {
    validateResolvableType(p, type);
    QueryName query = (QueryName) annotation;
    boolean encoded = query.encoded();

    Class<?> rawParameterType = Utils.getRawType(type);
    gotQueryName = true;
    if (Iterable.class.isAssignableFrom(rawParameterType)) {
      if (!(type instanceof ParameterizedType)) {
        throw parameterError(
            method,
            p,
            rawParameterType.getSimpleName()
                + " must include generic type (e.g., "
                + rawParameterType.getSimpleName()
                + "<String>)");
      }
      ParameterizedType parameterizedType = (ParameterizedType) type;
      Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
      Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations);
      return new ParameterHandler.QueryName<>(converter, encoded).iterable();
    } else if (rawParameterType.isArray()) {
      Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
      Converter<?, String> converter =
          retrofit.stringConverter(arrayComponentType, annotations);
      return new ParameterHandler.QueryName<>(converter, encoded).array();
    } else {
      Converter<?, String> converter = retrofit.stringConverter(type, annotations);
      return new ParameterHandler.QueryName<>(converter, encoded);
    }

  } else if (annotation instanceof QueryMap) {
    validateResolvableType(p, type);
    Class<?> rawParameterType = Utils.getRawType(type);
    gotQueryMap = true;
    if (!Map.class.isAssignableFrom(rawParameterType)) {
      throw parameterError(method, p, "@QueryMap parameter type must be Map.");
    }
    Type mapType = Utils.getSupertype(type, rawParameterType, Map.class);
    if (!(mapType instanceof ParameterizedType)) {
      throw parameterError(
          method, p, "Map must include generic types (e.g., Map<String, String>)");
    }
    ParameterizedType parameterizedType = (ParameterizedType) mapType;
    Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
    if (String.class != keyType) {
      throw parameterError(method, p, "@QueryMap keys must be of type String: " + keyType);
    }
    Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
    Converter<?, String> valueConverter = retrofit.stringConverter(valueType, annotations);

    return new ParameterHandler.QueryMap<>(
        method, p, valueConverter, ((QueryMap) annotation).encoded());

  } else if (annotation instanceof Header) {
    validateResolvableType(p, type);
    Header header = (Header) annotation;
    String name = header.value();

    Class<?> rawParameterType = Utils.getRawType(type);
    if (Iterable.class.isAssignableFrom(rawParameterType)) {
      if (!(type instanceof ParameterizedType)) {
        throw parameterError(
            method,
            p,
            rawParameterType.getSimpleName()
                + " must include generic type (e.g., "
                + rawParameterType.getSimpleName()
                + "<String>)");
      }
      ParameterizedType parameterizedType = (ParameterizedType) type;
      Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
      Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations);
      return new ParameterHandler.Header<>(name, converter).iterable();
    } else if (rawParameterType.isArray()) {
      Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
      Converter<?, String> converter =
          retrofit.stringConverter(arrayComponentType, annotations);
      return new ParameterHandler.Header<>(name, converter).array();
    } else {
      Converter<?, String> converter = retrofit.stringConverter(type, annotations);
      return new ParameterHandler.Header<>(name, converter);
    }

  } else if (annotation instanceof HeaderMap) {
    if (type == Headers.class) {
      return new ParameterHandler.Headers(method, p);
    }

    validateResolvableType(p, type);
    Class<?> rawParameterType = Utils.getRawType(type);
    if (!Map.class.isAssignableFrom(rawParameterType)) {
      throw parameterError(method, p, "@HeaderMap parameter type must be Map.");
    }
    Type mapType = Utils.getSupertype(type, rawParameterType, Map.class);
    if (!(mapType instanceof ParameterizedType)) {
      throw parameterError(
          method, p, "Map must include generic types (e.g., Map<String, String>)");
    }
    ParameterizedType parameterizedType = (ParameterizedType) mapType;
    Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
    if (String.class != keyType) {
      throw parameterError(method, p, "@HeaderMap keys must be of type String: " + keyType);
    }
    Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
    Converter<?, String> valueConverter = retrofit.stringConverter(valueType, annotations);

    return new ParameterHandler.HeaderMap<>(method, p, valueConverter);

  } else if (annotation instanceof Field) {
    validateResolvableType(p, type);
    if (!isFormEncoded) {
      throw parameterError(method, p, "@Field parameters can only be used with form encoding.");
    }
    Field field = (Field) annotation;
    String name = field.value();
    boolean encoded = field.encoded();

    gotField = true;

    Class<?> rawParameterType = Utils.getRawType(type);
    if (Iterable.class.isAssignableFrom(rawParameterType)) {
      if (!(type instanceof ParameterizedType)) {
        throw parameterError(
            method,
            p,
            rawParameterType.getSimpleName()
                + " must include generic type (e.g., "
                + rawParameterType.getSimpleName()
                + "<String>)");
      }
      ParameterizedType parameterizedType = (ParameterizedType) type;
      Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
      Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations);
      return new ParameterHandler.Field<>(name, converter, encoded).iterable();
    } else if (rawParameterType.isArray()) {
      Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
      Converter<?, String> converter =
          retrofit.stringConverter(arrayComponentType, annotations);
      return new ParameterHandler.Field<>(name, converter, encoded).array();
    } else {
      Converter<?, String> converter = retrofit.stringConverter(type, annotations);
      return new ParameterHandler.Field<>(name, converter, encoded);
    }

  } else if (annotation instanceof FieldMap) {
    validateResolvableType(p, type);
    if (!isFormEncoded) {
      throw parameterError(
          method, p, "@FieldMap parameters can only be used with form encoding.");
    }
    Class<?> rawParameterType = Utils.getRawType(type);
    if (!Map.class.isAssignableFrom(rawParameterType)) {
      throw parameterError(method, p, "@FieldMap parameter type must be Map.");
    }
    Type mapType = Utils.getSupertype(type, rawParameterType, Map.class);
    if (!(mapType instanceof ParameterizedType)) {
      throw parameterError(
          method, p, "Map must include generic types (e.g., Map<String, String>)");
    }
    ParameterizedType parameterizedType = (ParameterizedType) mapType;
    Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
    if (String.class != keyType) {
      throw parameterError(method, p, "@FieldMap keys must be of type String: " + keyType);
    }
    Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
    Converter<?, String> valueConverter = retrofit.stringConverter(valueType, annotations);

    gotField = true;
    return new ParameterHandler.FieldMap<>(
        method, p, valueConverter, ((FieldMap) annotation).encoded());

  } else if (annotation instanceof Part) {
    validateResolvableType(p, type);
    if (!isMultipart) {
      throw parameterError(
          method, p, "@Part parameters can only be used with multipart encoding.");
    }
    Part part = (Part) annotation;
    gotPart = true;

    String partName = part.value();
    Class<?> rawParameterType = Utils.getRawType(type);
    if (partName.isEmpty()) {
      if (Iterable.class.isAssignableFrom(rawParameterType)) {
        if (!(type instanceof ParameterizedType)) {
          throw parameterError(
              method,
              p,
              rawParameterType.getSimpleName()
                  + " must include generic type (e.g., "
                  + rawParameterType.getSimpleName()
                  + "<String>)");
        }
        ParameterizedType parameterizedType = (ParameterizedType) type;
        Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
        if (!MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(iterableType))) {
          throw parameterError(
              method,
              p,
              "@Part annotation must supply a name or use MultipartBody.Part parameter type.");
        }
        return ParameterHandler.RawPart.INSTANCE.iterable();
      } else if (rawParameterType.isArray()) {
        Class<?> arrayComponentType = rawParameterType.getComponentType();
        if (!MultipartBody.Part.class.isAssignableFrom(arrayComponentType)) {
          throw parameterError(
              method,
              p,
              "@Part annotation must supply a name or use MultipartBody.Part parameter type.");
        }
        return ParameterHandler.RawPart.INSTANCE.array();
      } else if (MultipartBody.Part.class.isAssignableFrom(rawParameterType)) {
        return ParameterHandler.RawPart.INSTANCE;
      } else {
        throw parameterError(
            method,
            p,
            "@Part annotation must supply a name or use MultipartBody.Part parameter type.");
      }
    } else {
      Headers headers =
          Headers.of(
              "Content-Disposition",
              "form-data; name="" + partName + """,
              "Content-Transfer-Encoding",
              part.encoding());

      if (Iterable.class.isAssignableFrom(rawParameterType)) {
        if (!(type instanceof ParameterizedType)) {
          throw parameterError(
              method,
              p,
              rawParameterType.getSimpleName()
                  + " must include generic type (e.g., "
                  + rawParameterType.getSimpleName()
                  + "<String>)");
        }
        ParameterizedType parameterizedType = (ParameterizedType) type;
        Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
        if (MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(iterableType))) {
          throw parameterError(
              method,
              p,
              "@Part parameters using the MultipartBody.Part must not "
                  + "include a part name in the annotation.");
        }
        Converter<?, RequestBody> converter =
            retrofit.requestBodyConverter(iterableType, annotations, methodAnnotations);
        return new ParameterHandler.Part<>(method, p, headers, converter).iterable();
      } else if (rawParameterType.isArray()) {
        Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
        if (MultipartBody.Part.class.isAssignableFrom(arrayComponentType)) {
          throw parameterError(
              method,
              p,
              "@Part parameters using the MultipartBody.Part must not "
                  + "include a part name in the annotation.");
        }
        Converter<?, RequestBody> converter =
            retrofit.requestBodyConverter(arrayComponentType, annotations, methodAnnotations);
        return new ParameterHandler.Part<>(method, p, headers, converter).array();
      } else if (MultipartBody.Part.class.isAssignableFrom(rawParameterType)) {
        throw parameterError(
            method,
            p,
            "@Part parameters using the MultipartBody.Part must not "
                + "include a part name in the annotation.");
      } else {
        Converter<?, RequestBody> converter =
            retrofit.requestBodyConverter(type, annotations, methodAnnotations);
        return new ParameterHandler.Part<>(method, p, headers, converter);
      }
    }

  } else if (annotation instanceof PartMap) {
    validateResolvableType(p, type);
    if (!isMultipart) {
      throw parameterError(
          method, p, "@PartMap parameters can only be used with multipart encoding.");
    }
    gotPart = true;
    Class<?> rawParameterType = Utils.getRawType(type);
    if (!Map.class.isAssignableFrom(rawParameterType)) {
      throw parameterError(method, p, "@PartMap parameter type must be Map.");
    }
    Type mapType = Utils.getSupertype(type, rawParameterType, Map.class);
    if (!(mapType instanceof ParameterizedType)) {
      throw parameterError(
          method, p, "Map must include generic types (e.g., Map<String, String>)");
    }
    ParameterizedType parameterizedType = (ParameterizedType) mapType;

    Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
    if (String.class != keyType) {
      throw parameterError(method, p, "@PartMap keys must be of type String: " + keyType);
    }

    Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
    if (MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(valueType))) {
      throw parameterError(
          method,
          p,
          "@PartMap values cannot be MultipartBody.Part. "
              + "Use @Part List<Part> or a different value type instead.");
    }

    Converter<?, RequestBody> valueConverter =
        retrofit.requestBodyConverter(valueType, annotations, methodAnnotations);

    PartMap partMap = (PartMap) annotation;
    return new ParameterHandler.PartMap<>(method, p, valueConverter, partMap.encoding());

  } else if (annotation instanceof Body) {
    validateResolvableType(p, type);
    if (isFormEncoded || isMultipart) {
      throw parameterError(
          method, p, "@Body parameters cannot be used with form or multi-part encoding.");
    }
    if (gotBody) {
      throw parameterError(method, p, "Multiple @Body method annotations found.");
    }

    Converter<?, RequestBody> converter;
    try {
      converter = retrofit.requestBodyConverter(type, annotations, methodAnnotations);
    } catch (RuntimeException e) {
      // Wide exception range because factories are user code.
      throw parameterError(method, e, p, "Unable to create @Body converter for %s", type);
    }
    gotBody = true;
    return new ParameterHandler.Body<>(method, p, converter);

  } else if (annotation instanceof Tag) {
    validateResolvableType(p, type);

    Class<?> tagType = Utils.getRawType(type);
    for (int i = p - 1; i >= 0; i--) {
      ParameterHandler<?> otherHandler = parameterHandlers[i];
      if (otherHandler instanceof ParameterHandler.Tag
          && ((ParameterHandler.Tag) otherHandler).cls.equals(tagType)) {
        throw parameterError(
            method,
            p,
            "@Tag type "
                + tagType.getName()
                + " is duplicate of parameter #"
                + (i + 1)
                + " and would always overwrite its value.");
      }
    }

    return new ParameterHandler.Tag<>(tagType);
  }

  return null; // Not a Retrofit annotation.
}
Copy the code

Then createRawCall is assembled into an okHttp3.call

private okhttp3.Call createRawCall() throws IOException {
  okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
  if (call == null) {
    throw new NullPointerException("Call.Factory returned null.");
  }
  return call;
}
Copy the code

I’m gonna go into the create method

okhttp3.Request create(Object[] args) throws IOException { @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types. ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers; int argumentCount = args.length; if (argumentCount ! = handlers.length) { throw new IllegalArgumentException( "Argument count (" + argumentCount + ") doesn't match expected count (" + handlers.length + ")"); } RequestBuilder requestBuilder = new RequestBuilder( httpMethod, baseUrl, relativeUrl, headers, contentType, hasBody, isFormEncoded, isMultipart); if (isKotlinSuspendFunction) { // The Continuation is the last parameter and the handlers array contains null at that index. argumentCount--; } List<Object> argumentList = new ArrayList<>(argumentCount); for (int p = 0; p < argumentCount; p++) { argumentList.add(args[p]); handlers[p].apply(requestBuilder, args[p]); } return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build(); }Copy the code

The okHttp3.call object can be easily created here because of the various records that precede the class

Data parsing
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException { ResponseBody rawBody = rawResponse.body(); // Remove the body's source (the only stateful object) so we can pass the response along. rawResponse = rawResponse .newBuilder() .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())) .build(); int code = rawResponse.code(); if (code < 200 || code >= 300) { try { // Buffer the entire body to avoid future I/O. ResponseBody bufferedBody = Utils.buffer(rawBody); return Response.error(bufferedBody, rawResponse); } finally { rawBody.close(); } } if (code == 204 || code == 205) { rawBody.close(); return Response.success(null, rawResponse); } ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody); Try {/ / data parsing T body = responseConverter. Convert (catchingBody); return Response.success(body, rawResponse); } catch (RuntimeException e) { catchingBody.throwIfCaught(); throw e; }}Copy the code

If convert is an interface, trace responseConverter to see where the value was assigned

OkHttpCall(
    RequestFactory requestFactory,
    Object[] args,
    okhttp3.Call.Factory callFactory,
    Converter<ResponseBody, T> responseConverter) {
  this.requestFactory = requestFactory;
  this.args = args;
  this.callFactory = callFactory;
  this.responseConverter = responseConverter;
}
Copy the code

Assign when the OkHttpCall is created

HttpServiceMethod(
    RequestFactory requestFactory,
    okhttp3.Call.Factory callFactory,
    Converter<ResponseBody, ResponseT> responseConverter) {
  this.requestFactory = requestFactory;
  this.callFactory = callFactory;
  this.responseConverter = responseConverter;
}

@Override
final @Nullable ReturnT invoke(Object[] args) {
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
  return adapt(call, args);
}
Copy the code

See where HttpServiceMethod was created

CallAdapted(
    RequestFactory requestFactory,
    okhttp3.Call.Factory callFactory,
    Converter<ResponseBody, ResponseT> responseConverter,
    CallAdapter<ResponseT, ReturnT> callAdapter) {
  super(requestFactory, callFactory, responseConverter);
  this.callAdapter = callAdapter;
}
Copy the code

Callcaller is created in the method parseAnnotations

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) { ... if (! isKotlinSuspendFunction) { return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); } else if (continuationWantsResponse) { //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object. return  (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForResponse<>( requestFactory, callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter); } else { //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object. return (HttpServiceMethod<ResponseT, ReturnT>) new SuspendForBody<>( requestFactory, callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter, continuationBodyNullable); }}Copy the code

Look at responseConverter where the assignment is made

Converter<ResponseBody, ResponseT> responseConverter =
    createResponseConverter(retrofit, method, responseType);
Copy the code

Enter the createResponseConverter method

private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter( Retrofit retrofit, Method method, Type responseType) { Annotation[] annotations = method.getAnnotations(); try { return retrofit.responseBodyConverter(responseType, annotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw methodError(method, e, "Unable to create converter for %s", responseType); }}Copy the code

Enter responseBodyConverter method

public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
  return nextResponseBodyConverter(null, type, annotations);
}
Copy the code

Enter the nextResponseBodyConverter method

public <T> Converter<ResponseBody, T> nextResponseBodyConverter( @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) { Objects.requireNonNull(type, "type == null"); Objects.requireNonNull(annotations, "annotations == null"); int start = converterFactories.indexOf(skipPast) + 1; for (int i = start, count = converterFactories.size(); i < count; i++) { Converter<ResponseBody, ? > converter = converterFactories.get(i).responseBodyConverter(type, annotations, this); if (converter ! = null) { return (Converter<ResponseBody, T>) converter; } } StringBuilder builder = new StringBuilder("Could not locate ResponseBody converter for ") .append(type) .append(".\n"); if (skipPast ! = null) { builder.append(" Skipped:"); for (int i = 0; i < start; i++) { builder.append("\n * ").append(converterFactories.get(i).getClass().getName()); } builder.append('\n'); } builder.append(" Tried:"); for (int i = start, count = converterFactories.size(); i < count; i++) { builder.append("\n * ").append(converterFactories.get(i).getClass().getName()); } throw new IllegalArgumentException(builder.toString()); }Copy the code

The process is a little bit like the one above.

val retrofit = Retrofit.Builder()
        .baseUrl("https://api.github.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build()
Copy the code

AddConverterFactory (GsonConverterFactory. The create ()), if at the time of creation to add this line can use gson parsing.