The foreword 0.

Retrofit is a web request framework based on OKHTTP, which is basically a wrapper around OKHTTP, and it’s also made by Square, it’s made by Square, it’s got to be a good product, and when I got some time recently, I looked at how it works,

1. Basic use

  interface TestService
    {

        @GET("/")
        fun test(): Call<String>
    }
    
class ResponseConvertFactory : Converter.Factory()
    {
        override fun responseBodyConverter(type: Type? , annotations: Array<out Annotation>? , retrofit: Retrofit?) : Converter<ResponseBody, String>? {return Converter<ResponseBody, String> {
                it.string()
            }

        }

        override fun stringConverter(type: Type? , annotations: Array<out Annotation>? , retrofit: Retrofit?) : Converter<*, String>? {return super.stringConverter(type, annotations, retrofit)
        }
    }


 val retrofit = Retrofit.Builder().baseUrl("http://www.baidu.com")
                .addConverterFactory(ResponseConvertFactory())
                .build()
        val testService = retrofit.create(TestService::class.java)
        val call = testService.test() call.enqueue(object : Callback<String> { override fun onFailure(call: Call<String>? , t: Throwable?) { } override fun onResponse(call: Call<String>? , response: Response<String>?) { println("response is ${response? .body()}")}})Copy the code

This is a simple asynchronous HTTP GET request, because only the analysis of the process, by simple entry on the line, I think it is most important to see the code to understand the process and its design pattern, do not need to be too severe.

2. The creation of a Retrofit

Retrofit was created using the Builder model

val retrofit = Retrofit.Builder().baseUrl("http://www.baidu.com")
                .addConverterFactory(ResponseConvertFactory())
                .build()
Copy the code

Look directly at retrofit.Builder’s build method

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

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

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

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

      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories =
          new ArrayList<>(1 + this.converterFactories.size());

      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);

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

Here we pass in a list of callAdapterFactory, which is the list of callAdapterFactory from the Builder, which is the argument you passed in addCallAdapterFactory (), The factory passed into the Builder’s CallAdapter. factory list is followed by a default CallAdapterFactory, which is the platform parameter. The platform is passed in when you instantiate the Builder

 private static Platform findPlatform() {
    try {
      Class.forName("android.os.Build");
      if(Build.VERSION.SDK_INT ! = 0) {return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }
Copy the code

If it’s running on Android, it’s going to come back with Android. Look at Android

static class Android extends Platform {
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      returnnew ExecutorCallAdapterFactory(callbackExecutor); } static class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable r) { handler.post(r); }}}Copy the code

You can see CallAdapterFactory ExecutorCallAdapterFactory is returned by the platform. More on his role later.

There is also a list of convert. factories, where a BuiltInConverters is added and then the list of convert. factories exists in the Builder. The convert. Factory is primarily used to generate conversions to Convert HTTP request bodies and response messages.

2. Preparation before issuing a request

       val testService = retrofit.create(TestService::class.java)
        val call = testService.test()
Copy the code

With these two methods, an instance is generated that implements the TestService interface, and the method calling this interface returns a Call. Let’s see how this is done, how an instance of the interface is generated, and how the Call is generated.

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return(T) Proxy.newProxyInstance(service.getClassLoader(), new Class<? >[] { service }, newInvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public 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);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            returnserviceMethod.adapt(okHttpCall); }}); }Copy the code

This is obviously a dynamic proxy. If you are not clear about dynamic proxies, you can refer to my previous article removing the wheel series – dynamic proxies. If the method does not come from an interface but from an object method, then call it directly. If it comes from an interface, then you can see that the method is a serviceMethod. Take a look at the loadServiceMethod method

ServiceMethod<? ,? > loadServiceMethod(Method method) { ServiceMethod<? ,? > result = serviceMethodCache.get(method);if(result ! = null)return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if(result == null) { result = new ServiceMethod.Builder<>(this, method).build(); serviceMethodCache.put(method, result); }}return result;
  }
Copy the code

It’s very simple, if there is a ServiceMethod in the cache, then pull out a ServiceMethod, otherwise create a ServiceMethod and add it to the cache, again in Builder mode, look at the build method for ServiceMethod.Builder

 public ServiceMethod build() {
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      if (responseType == Response.class || responseType == okhttp3.Response.class) {
        throw methodError("'"
            + Utils.getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
      }
      responseConverter = createResponseConverter();

      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }

      if (httpMethod == null) {
        throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }

      if(! hasBody) {if (isMultipart) {
          throw methodError(
              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }
        if (isFormEncoded) {
          throw methodError("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; p < parameterCount; p++) {
        Type parameterType = parameterTypes[p];
        if (Utils.hasUnresolvableType(parameterType)) {
          throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
              parameterType);
        }

        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
        if (parameterAnnotations == null) {
          throw parameterError(p, "No Retrofit annotation found.");
        }

        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }

      if(relativeUrl == null && ! gotUrl) { throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
      }
      if(! isFormEncoded && ! isMultipart && ! hasBody && gotBody) { throw methodError("Non-body HTTP method cannot contain @Body.");
      }
      if(isFormEncoded && ! gotField) { throw methodError("Form-encoded method must contain at least one @Field.");
      }
      if(isMultipart && ! gotPart) { throw methodError("Multipart method must contain at least one @Part.");
      }

      return new ServiceMethod<>(this);
    }
Copy the code

Take a look at the createCallAdapter method

 private CallAdapter<T, R> createCallAdapter() {
      Type returnType = method.getGenericReturnType();
      if (Utils.hasUnresolvableType(returnType)) {
        throw methodError(
            "Method return type must not include a type variable or wildcard: %s".returnType);
      }
      if (returnType == void.class) {
        throw methodError("Service methods cannot return void.");
      }
      Annotation[] annotations = method.getAnnotations();
      try {
        //noinspection unchecked
        return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
      } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(e, "Unable to create call adapter for %s".returnType); }}Copy the code

The callAdapter that calls RetroFIT returns a callAdapter based on the Type and annotation of the value returned by method. Take a look at retrofit’s callAdapter method.

public CallAdapter<? ,? > callAdapter(TypereturnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations); } public CallAdapter<? ,? > nextCallAdapter(@Nullable CallAdapter.Factory skipPast, TypereturnType,
      Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(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

I’m going to iterate through Retrofit’s CallAdapterFactory list, and I’m going to return the appropriate calladapter, so remember the order of the CallAdapterFactory list, The default calladapterfactory ExecutorCallAdapterFactory is in the final, so if you’re building Retrofit when added other Calladapter, if conditions are met, is to be able to intercept. RxJavaCallAdapterFactory. The create () this familiar, because it eventually return to observables instead of Call. The CallAdapter converts OkhttpCall to other data structures, and the default is to convert OkhttpCall to ExecutorCallbackCall. Back to the build method

responseConverter = createResponseConverter();
Copy the code

It’s similar to returning the CallAdapter, except that this is the ResponseConvert that is returned next

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

Iterate through the method annotations to get the corresponding parameters, but I won’t go into details.

Then an OkhttpCall is generated

OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
Copy the code

You’ll notice that this OkhttpCall implements OkHttp’s Call interface, looking at its enque method

@Override public void enqueue(final Callback<T> callback) {
    checkNotNull(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 { call = rawCall = createRawCall(); } catch (Throwable t) { throwIfFatal(t); failure = creationFailure = t; }}}if(failure ! = null) { callback.onFailure(this, failure);return;
    }

    if (canceled) {
      call.cancel();
    }

    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return; } try { callback.onResponse(OkHttpCall.this, response); } catch (Throwable t) { t.printStackTrace(); } } @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) { t.printStackTrace(); }}}); }Copy the code

Here is an Okhttp call generated, and then Okhttp network request flow, if Okhttp is not familiar with you can see my previous article unwheel series – OKHTTP3 source code analysis.

Go back to the InvaoctaionHandler and finally call the serviceMethod. Adapte method,

T adapt(Call<R> call) {
    return callAdapter.adapt(call);
  }
Copy the code

Can see is the call of calladapter adapt methods, that the default ExecutorCallAdapterFactory calladapter are made

final class ExecutorCallAdapterFactory extends CallAdapter.Factory { final Executor callbackExecutor; ExecutorCallAdapterFactory(Executor callbackExecutor) { this.callbackExecutor = callbackExecutor; } @Override public CallAdapter<? ,? > get(TypereturnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) ! = Call.class) {return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    returnnew CallAdapter<Object, Call<? >>() { @Override public TyperesponseType() {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
        returnnew ExecutorCallbackCall<>(callbackExecutor, call); }}; }Copy the code

You can see that an anonymous inner class is returned, and his Adapt method actually returns the ExecutorCallbackCall

static final class ExecutorCallbackCall<T> implements Call<T> {
    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) {
      checkNotNull(callback, "callback == null");

      delegate.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              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); }}}); } @Override public void onFailure(Call
      
        call, final Throwable t) { callbackExecutor.execute(new Runnable() { @Override public void run() { callback.onFailure(ExecutorCallbackCall.this, t); }}); }}); } @Override public boolean isExecuted() { return delegate.isExecuted(); } @Override public Response
       
         execute() throws IOException { return delegate.execute(); } @Override public void cancel() { delegate.cancel(); } @Override public boolean isCanceled() { return delegate.isCanceled(); } @SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone. @Override public Call
        
          clone() { return new  ExecutorCallbackCall<>(callbackExecutor, delegate.clone()); } @Override public Request request() { return delegate.request(); }}
        
       
      Copy the code

As you can see, this implements the Call interface of Okhttp. It also holds a CallBackExecutor and the previously generated OkHttpCall. You can see that the enqueu method that calls it actually executes the network request is okHttpCall. And this callbackExecutor is actually

static class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable r) { handler.post(r); }}Copy the code

Obviously, this ensures that the result of the callback is on the main thread.

Picture from