“This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!”

Retrofit comprehensive parsing

A type-safe HTTP client for Android and Java

Source analysis version: implementation ‘com. Squareup. Retrofit2: retrofit: 2.9.0’

The use of the Retrofit

About the use of the Retrofit, here no longer than the interpretation of the, believe that as long as it is for Android development will use Retrofit square. The dead simple. IO/Retrofit/the use of the most simple way is as follows:

interface GitHubService {
    @GET("users/{user}/repos")
    fun listRepos(@Path("user") user:String?).:Call<List<Repo>>
}
val retrofit = Retrofit.Builder()
            .baseUrl("https://api.github.com/")
            .addConverterFactory(GsonConverterFactory.create()) // GSON's conversion plant
            .build()

        retrofit.create(GitHubService::class.java).listRepos("octocat")
            .enqueue(object :Callback<List<Repo>>{
                override fun onResponse(call: Call<List<Repo>>, response: Response<List<Repo> >){ println(response.body()? .size) }override fun onFailure(call: Call<List<Repo>>, t: Throwable) {
                    TODO("Not yet implemented")}})Copy the code

Look at the source code, focus on a line, until you understand the line.

So let’s focus oncreateMethod, this is route A, and then this isenqueueThis is route B.

Analyze from the Create method

The following code builds a Retrofit instance using the Builder pattern

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

The retrofit.create() method is initialized with the following code: Using Java’s dynamic proxy method, to pass the Class proxy method, note that the Class must be an interface. Dynamic proxies in Java can only proxy interfaces. Let’s first look at the first step: what does the validateServiceInterface method do?

  public <T> T create(final Class<T> service) {
    //1. Initialize the service interface
    validateServiceInterface(service);
    //2. Return the proxy object
    return (T)
        // Use the dynamic proxy provided by Java
        Proxy.newProxyInstance(
            service.getClassLoader(),
            newClass<? >[] {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 the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args); } args = args ! =null ? args : emptyArgs;
                returnplatform.isDefaultMethod(method) ? platform.invokeDefaultMethod(method, service, proxy, args) : loadServiceMethod(method).invoke(args); }}); }Copy the code

ValidateServiceInterface validateServiceInterface validateServiceInterface validateServiceInterface validateServiceInterface validateServiceInterface validateServiceInterface

  private void validateServiceInterface(Class
        service) {
  	// The class must be an interface. If not, an error will be reported
    if(! service.isInterface()) {throw new IllegalArgumentException("API declarations must be interfaces.");
    }
    // Add the interface to the queueDeque<Class<? >> check =new ArrayDeque<>(1);
    check.add(service);
    while(! check.isEmpty()) {// Fetch the interface class from the queueClass<? > candidate = check.removeFirst();GithubService
      
        = GithubService
      
      if(candidate.getTypeParameters().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());
      }
      // Check whether the interface has inheritance
      Collections.addAll(check, candidate.getInterfaces());
    }
	// Switch, validate15, development and formal can be quickly verified
    if (validateEagerly) {
      Platform platform = Platform.get();
      for (Method method : service.getDeclaredMethods()) {
          // Filter the default and static methods of the interface
        if(! platform.isDefaultMethod(method) && ! Modifier.isStatic(method.getModifiers())) {// Validates and loads the methods of the interface classloadServiceMethod(method); }}}}Copy the code

In fact, validatezh is mainly used to verify all method configurations in the interface during initialization, and is usually set to true during the development phase. Retrofit provides the following interpretation:

 /**
     * When calling {@link #create} on the resulting {@linkRetrofit} instance, eagerly validate the * configuration of all methods in the supplied interface. When in the result {@linkCall {on Retrofit} instance@link#create} is eagerly verifying the configuration of all methods in the provided interface. * /
    public Builder validateEagerly(boolean validateEagerly) {
      this.validateEagerly = validateEagerly;
      return this;
    }
Copy the code

After the initialization validation is passed, the proxy object is generated using the dynamic proxy pattern in Java.

A dynamic proxy

The invoke method is invoked when a method on the Proxy object is invoked. This is also the entry point at the heart of Retrofit.

    return (T)
        // Use the dynamic proxy provided by JavaProxy.newProxyInstance( service.getClassLoader(), new Class<? >[] {service}, new InvocationHandler() {private final Platform platform = Platform.get(a);private final Object[] emptyArgs = new Object[0];

              @Override
              public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                  throws Throwable {
                // If the method is a method from Object then defer to normal invocation.
                // The method of the Object class
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args); } args = args ! =null ? args : emptyArgs;
                return platform.isDefaultMethod(method) // consider java8 compatibility issues
                    ? platform.invokeDefaultMethod(method, service, proxy, args) // Call the default method directly if it is the default method: loadServiceMethod(method).invoke(args); }});Copy the code

The principle of dynamic proxy is very simple and is actually equivalent to the following code: focus on the Invoke callback method

class ProxyGitHubService : GitHubService {
    val invocationHandler:InvocationHandler = InvocationHandler(object : (Any, Method, Array<Any>) -> Any{
        override fun invoke(p1: Any, p2: Method, p3: Array<Any>): Any {
            // If the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args); } args = args ! =null ? args : emptyArgs;
                returnplatform.isDefaultMethod(method) ? platform.invokeDefaultMethod(method, service, proxy, args) : loadServiceMethod(method).invoke(args); }})override fun listRepos(user: String?).: Call<List<Repo>> {
        val method = GitHubService::class.java.getDeclaredMethod("listRepos",String::class.java)
        invocationHandler.invoke(this,method,user)
    }
}
Copy the code

The final core method is the loadServiceMethod method, which eventually calls the Invoke () method. Now what does loadServiceMethod do?

loadServiceMethod

The code of loadServiceMethod is as follows: Obtains ServiceMethod from the Map set and returns it. Retrifit does the cache processing. The next call directly obtains it from the cache. So what does this ServiceMethod class do?

ServiceMethod<? > loadServiceMethod(Method method) {// Retrieve the cache. ServiceMethodCache is a Map setServiceMethod<? > result = serviceMethodCache.get(method);
    // Return it if it exists
    if(result ! =null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      // If not, it is stored in the cache
      if (result == null) {
        // Handle the interface method
        result = ServiceMethod.parseAnnotations(this, method); serviceMethodCache.put(method, result); }}return result;
  }
Copy the code

Let’s take a look at the ServiceMethod class, an abstract class that is not implemented by the Invoke method. We can see that we have returned an instance object from the ServiceMethod class using parseAnnotations. We found the HttpServiceMethod class that implements ServiceMethod. Let’s focus on the invoke method,

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    // Parse the annotation of the method, return the type of value, and pass the parameters
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
    // Get the return value type of the 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);
  }

  abstract @Nullable T invoke(Object[] args);
}
Copy the code

HttpServiceMethod

Let’s first look at the implementation of the Invoke method:

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

From the invoke method, you can see that there are two lines:OkHttpCallAs well asadaptThrough the source line analysis diagram as follows, the two lines we first look at OkHttpCall this line C.

Line C -> OkHttpCall

As you can see, OkHttpCall implements the Call interface. If the Call interface is familiar, in our use of Retrofit, the interface’s methods will return a Call

interface GitHubService {
    @GET("users/{user}/repos")
    fun listRepos(@Path("user") user:String?).:Call<List<Repo>>
}
Copy the code

That said, OkHttpCall implements the enqueue() method. The code is as follows:

@Override
  public void enqueue(final Callback<T> callback) {
    .........
    // Call instance of okhttp
    okhttp3.Call call;
    Throwable failure;
    synchronized (this) {... executed =true;
      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          call = rawCall = createRawCall();
        } catch(Throwable t) { throwIfFatal(t); failure = creationFailure = t; }}}...// The enqueue method of okHTTP is finally called
    call.enqueue(
        new okhttp3.Callback() {
          @Override
          public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
            Response<T> response;
            try {
              // Parse the returned result
              response = parseResponse(rawResponse);
            } catch (Throwable e) {
              throwIfFatal(e);
              callFailure(e);
              return; }... }@Override
          public void onFailure(okhttp3.Call call, IOException e) {
            callFailure(e);
          }

          privatevoid callFailure(Throwable e) { ....... }}); }Copy the code

There are two new lines in enqueue:createRawCall()Method (creates the call object for OKHTTP) andparseResponse()Method (parse the return value).

createRawCall

First analyze how line E creates an object instance of okhttp.call. The following code is primarily created using the callFactory and requestFactory and is passed in when the OkHttpCall instance is 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;
  }
  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

We look up the link. At this time, the circuit diagram is as follows:Lookup process: HttpServiceMethod invoke – > HttpServiceMethod. ParseAnnotations () code is as follows: callFactory and requestFactory consist of external passed, continue to find up

  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
      ......
          okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if(! isKotlinSuspendFunction) {returnnew CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); }... }Copy the code

Here’s the code: We’ve seen the RequestFactory, finally found it, let’s go in and see what it’s done.

abstract class ServiceMethod<T> {
  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.");
    }

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

RequestFactory: You can see that the RequestFactory mainly handles annotations to the methods of the interface class Service.class

    RequestFactory build() {
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation); }... }Copy the code

When the processing is complete, the following values are obtainedWe call back to line EIs called in the createRawCall() methodrequestFactory.create()Methods.

  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

The requestFactory.create() code looks like this: the end result is okHttp3.request

  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; . 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

Line E is okHttpClient.newCall (Request). This is the SAME API that okHTTP uses.OK, requestFactory is a requestFactory that handles the annotations in the method of the service class interface, determines get, POST, etc., and then returns okHttp3.request.

CallFactory (retrofit.callFactory); build (okHttpClient)

retrofit.callFactory

    public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) { callFactory = new OkHttpClient(); }... }Copy the code

Let’s take a look at the wiring: this will make the process very clear

parseResponse

Let’s see how parseResponse line F is executed. The code is as follows (in the OkHttpCall class) : this is finally handled by the responseConverter convert method

  Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();
    / /...
    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      T body = responseConverter.convert(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      // If the underlying source threw an exception, propagate that rather than indicating it was
      // a runtime exception.
      catchingBody.throwIfCaught();
      throwe; }}//responseConverter is also created in the OkHttpCall constructor
    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

So we have to look up: We have returned the responseConverter in the parseAnnotations method of HttpServiceMethod

  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    .........

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

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if(! isKotlinSuspendFunction) {returnnew CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); }...Copy the code

Let’s take a look at createResponseConverter method, called the retrofit. ResponseBodyConverter

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

The code looks like this: we’re actually going to iterate over the converterFactories collection, which stores the Converters, and we’re going to find out where we added the Converters from

  public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
    return nextResponseBodyConverter(null, type, annotations); }...public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
      @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
	.....
    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) {
        //noinspection unchecked
        return(Converter<ResponseBody, T>) converter; }}}Copy the code

Found in the Builder, there is a method to set addConverterFactory

    /** Add converter factory for serialization and deserialization of objects. */
    public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
      return this;
    }

Copy the code

I don’t know if you remember when we started with Retorfit, you see it’s addConverterFactory. We added GsonConverterFactory, a Javabean class that uses Gson to parse json data into Settings.

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

OK, so the whole process of OkHttpCall thought is combed clear. Let’s look at the flow chart again. There are a lot of design patterns and good code to learn from Retrofit’s source code

Line D -> ADAPT

Let’s go back to the Invoke method of HttpServiceMethod to see the concrete implementation of ADAPT.

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

Adapt is an abstract method of HttpServiceMethod

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

So how does it work? Let’s go back to parseAnnotations, which returns an instance of the HttpServerMethod, so adapt must be implemented.

 if(! isKotlinSuspendFunction) {// Do we use kotlin's suspend tag to determine whether or not it is used
      returnnew CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); }...Copy the code

Let’s look at the implementation of CallAppstore, en….. Oddly enough, the callAdapter is passed in from the outside, and the callAdapter passing in from the outside calls the adapt method

  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) {
      returncallAdapter.adapt(call); }}Copy the code

We need to go back to parseAnnotations and see this code:

. CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, adapterType, annotations); .if(! isKotlinSuspendFunction) {returnnew CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); }...Copy the code

The createCallAdapter method was called:…. The callAdapter returned by this method is returned by the callAdapter of retrofit.

  private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
      Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
    try {
      //noinspection unchecked
      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

Continuing back to Retrofit, the code looks like this: In fact, similar to the responseBodyConverter, which is user-definable, we can see that in the nextCallAdapter as long as the traversal adapter is not null it will return to terminate the traversal, So Retrofit can only have one CallAdapter, and Retrofit must have a default CallAdapter because we didn’t set it when we used it

  publicCallAdapter<? ,? > callAdapter(Type returnType, Annotation[] annotations) {return nextCallAdapter(null, returnType, annotations);
  }

  publicCallAdapter<? ,? > nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {
    .....
    int start = callAdapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
      / / get CallAdapterCallAdapter<? ,? > adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
      if(adapter ! =null) {
        returnadapter; }}... }Copy the code

Let’s go to the retrofit.Builder.build () method and look at it as follows:

public Retrofit build() {
      .......
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        // Get the thread pool. // Get the thread pool
        callbackExecutor = platform.defaultCallbackExecutor();
      }
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories); callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor)); . }Copy the code

We entered the defaultCallAdapterFactories Platform

// Obtain the platform based on the JVM name
private static Platform findPlatform() {
    return "Dalvik".equals(System.getProperty("java.vm.name"))? new Android()//
        : new Platform(true);
  }  
List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
      @Nullable Executor callbackExecutor) {
    DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
    return hasJava8Types
        ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
        : singletonList(executorFactory);
  }
   DefaultCallbackExecutor's thread pool executes in the main thread
    static final class Android extends Platform {
    Android() {
      super(Build.VERSION.SDK_INT >= 24);
    }

    @Override
    public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    @Nullable
    @OverrideObject invokeDefaultMethod( Method method, Class<? > declaringClass, Objectobject, 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 {
      // Handler in the main thread
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override
      publicvoid execute(Runnable r) { handler.post(r); }}}Copy the code

Look again at, DefaultCallAdapterFactory, how to implement CallAdapter.

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  private final @Nullable Executor callbackExecutor;

  DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor;
  }

  @Override
  public @NullableCallAdapter<? ,? >get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
    // Check that the service interface method returns a Call. If not, return null and continue the loop
    if(getRawType(returnType) ! = Call.class) {
      return null; }...// Return the CallAdapter instance
    returnnew 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); }}; }// Wrap the Call
  static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;
    // Pass a thread pool to delegateCall
    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }

    @Override
    public void enqueue(final Callback<T> callback) {
      .....
      delegate.enqueue(
          new Callback<T>() {
            @Override
            public void onResponse(Call<T> call, final Response<T> response) {
              // Make a thread switch to execute the callback in the main thread
              callbackExecutor.execute(
                  () -> {
                    if (delegate.isCanceled()) {
                      // Emulate OkHttp's behavior of throwing/delivering an IOException on
                      // cancellation.
                      callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                    } else {
                      callback.onResponse(ExecutorCallbackCall.this, response); }}); }... }); }}Copy the code

The default CallAdapter actually does one thing, which is to switch threads and make a callback in the main thread. So how do you customize the CallAdapter? We know that the default Retrofit interface method returns a value of Call, so if we want to use RxJava, how to adapt?

    @GET("users/{user}/repos")
    fun listReposRx(@Path("user") user:String?).:Single<List<Repo>>
    
    
     val retrofit = Retrofit.Builder()
            .baseUrl("https://api.github.com/")
            .addConverterFactory(GsonConverterFactory.create()) // GSON's conversion plant
            .addCallAdapterFactory(RxJava3CallAdapterFactory.create())// Add RxJava support
            .build()
    retrofit.create(GitHubService::class.java).listReposRx("octocat")
            .subscribe()        
Copy the code

So how does the RxJavaCallAdapter match the RxJava return type?

@Override
  public @NullableCallAdapter<? ,? >get( Type returnType, Annotation[] annotations, Retrofit retrofit) { Class<? > rawType = getRawType(returnType);// It matches the return types it has
    if (rawType == Completable.class) {
      // Completable is not parameterized (which is what the rest of this method deals with) so it
      // can only be created with a single configuration.
      return new RxJava3CallAdapter(
          Void.class, scheduler, isAsync, false.true.false.false.false.true);
    }

    boolean isFlowable = rawType == Flowable.class;
    boolean isSingle = rawType == Single.class;// The example above uses Single
    boolean isMaybe = rawType == Maybe.class;
    if(rawType ! = Observable.class&&! isFlowable && ! isSingle && ! isMaybe) {return null; }... }Copy the code

OK, so the complete process of Retrofit is as follows, through the wiring way to clear the source code.