preface

This article is based on Retrofit version 2.1.0, and it will be analyzed from the use of Retrofit into its source code. Retrofit is a RESTful HTTP Web request framework wrapped around OKHTTP. That is, it only takes care of the network interface configuration and encapsulation of the calls, leaving okHTTP to do the actual low-level calls. It can be used in the form of annotations to configure the address of the request, request parameters, etc., can also add custom interceptors, network interceptors and data converters for processing and extension.

Source code analysis

Start with usage.

Create a RetroFIT instance

Retrofit retrofit = new Retrofit.Builder()
        baseUrl("https://heyunjian.leanapp.cn/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();
Copy the code

Formally, retrofit was created using the Builder pattern. Let’s get into its source code.

Public Final class Retrofit {private final Map<Method, ServiceMethod<? ,? >> serviceMethodCache = new ConcurrentHashMap<>(); // Network requester Factory final okHttp3.call.factory callFactory; // Final HttpUrl baseUrl; // Final List<Converter.Factory> converterFactories; // Final List< calladapterfactory > adapterFactories; // Final Executor callbackExecutor; final boolean validateEagerly; Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl, List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories, Executor callbackExecutor, boolean validateEagerly) { this.callFactory = callFactory; this.baseUrl = baseUrl; this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site. this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site. this.callbackExecutor = callbackExecutor; this.validateEagerly = validateEagerly; } @SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
  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, 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.callAdapter.adapt(okHttpCall); }}); }... public static final class Builder { private final Platform platform; private okhttp3.Call.Factory callFactory; private HttpUrl baseUrl; private final List<Converter.Factory> converterFactories = new ArrayList<>(); private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>(); private Executor callbackExecutor; private boolean validateEagerly; Builder(Platform platform) { this.platform = platform; // 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()); } publicBuilder() { this(Platform.get()); } Builder(Retrofit retrofit) { platform = Platform.get(); callFactory = retrofit.callFactory; baseUrl = retrofit.baseUrl; converterFactories.addAll(retrofit.converterFactories); adapterFactories.addAll(retrofit.adapterFactories); // Remove the default, platform-aware call adapter added by build(). adapterFactories.remove(adapterFactories.size() - 1); callbackExecutor = retrofit.callbackExecutor; validateEagerly = retrofit.validateEagerly; }... public Retrofitbuild() {
      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> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

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

      returnnew Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); }}}Copy the code

Member variables in the Retrofit#Builder class are essentially the same as those in Retrofit, which is a characteristic of the Builder pattern. Retrofit member variables are already commented in the source code. In Builder, we mainly look at Platform.

Subclasses of Platform:

There are two main subclasses that correspond to the platforms retrofit supports: Android and Java8.

Platform subclass Android source code:

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

    @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
      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

MainThreadExecutor static inner class creates a handler for the main thread that returns the result of the request processing to the Android main thread.

retrofit.create(CSDNAPIService.class);
Copy the code

Enter the create method source code

Retrofit#create

 @SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
  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, 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.callAdapter.adapt(okHttpCall); }}); }Copy the code

As you can see, dynamic proxies are used above. Proxy is the class object created by reflection, method is the method the object calls, and args is the parameter of the method to call. The loadServiceMethod method is analyzed.

Retrofit#loadServiceMethod

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

A serviceMethodCache is a cache that first fetches data from the cache. There’s no thread locking. Code for calling ServiceMethod.

result = new ServiceMethod.Builder<>(this, method).build();
Copy the code

The main work is in build method,

ServiceMethod#build

    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

Focus on line 11, where you parse the various configuration annotations for use.

ServiceMethod#parseMethodAnnotation

    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);
        if(! Void.class.equals(responseType)) { throw methodError("HEAD method must use Void as response type."); }}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("@Headers annotation is empty.");
        }
        headers = parseHeaders(headersToParse);
      } else if (annotation instanceof Multipart) {
        if (isFormEncoded) {
          throw methodError("Only one encoding annotation is allowed.");
        }
        isMultipart = true;
      } else if (annotation instanceof FormUrlEncoded) {
        if (isMultipart) {
          throw methodError("Only one encoding annotation is allowed.");
        }
        isFormEncoded = true; }}Copy the code

You can see the notes above.

Analyzing RetroFIT’s synchronous and asynchronous request processes:

An asynchronous request

OkHttpCall#enqueue

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

Looking at line 15, createRawCall() creates Call, an interface whose subclasses include RealCall.

public interface Call extends Cloneable

final class RealCall implements Call
Copy the code

Go to the OkHttpCall#createRawCall method

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

It creates the HTTP request object with parameters, then creates the Call, and returns it.

Analyzing the newCall method, which is the callFactory method, look at Factory

interface Factory {
    Call newCall(Request request);
  }
Copy the code

It’s implemented by OkHttpClient, so it goes to OkHttpClient’s newCall

OkHttpClient#newCall

@Override public Call newCall(Request request) {
    return new RealCall(this, request, false/ *for web socket */);
  }
Copy the code

RealCall is now analyzed. RealCall calls enQueue and execute methods for both asynchronous and synchronous requests.

Let’s look at the asynchronous enqueue method RealCall#enqueue

@Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }
Copy the code

Look at line 7. AsyncCall is a Runnable task that is submitted to the thread pool for execution. ResponseCallback is a callback to the result of an asynchronous request.

final class AsyncCall extends NamedRunnable

public abstract class NamedRunnable implements Runnable
Copy the code

ResponseCallback is a Callback

public interface Callback {

  void onFailure(Call call, IOException e);

  void onResponse(Call call, Response response) throws IOException;
}
Copy the code

AsyncCall is a Runnable task, so what does it do?

AsyncCall#execute

@Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else{ responseCallback.onFailure(RealCall.this, e); } } finally { client.dispatcher().finished(this); }}Copy the code

The above line 4 getResponseWithInterceptorChain returned to the result of the request, but it happened in the whole process of request. We’ll look at how it is responsible for the chain of call interceptors and how it calls callback methods based on the response results.

AsyncCall#execute overrides the execute method in NamedRunnable.

public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}
Copy the code

You can see that the execute method is called in the run method, the main work of the task. And what is the main job? That’s what AsyncCall#execute does above.

Go back to RealCall#enqueue and enter the enqueue for the Dispatcher on line 7

Dispatcher#enqueue

  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else{ readyAsyncCalls.add(call); }}Copy the code

RunningAsyncCalls saves tasks that are executed asynchronously. If the number of concurrent tasks does not exceed the maximum number of times that the thread pool can execute them, they are directly placed into the thread pool.

/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */ private final Deque
      
        runningAsyncCalls = new ArrayDeque<>(); . private int maxRequests = 64; private int maxRequestsPerHost = 5;
      Copy the code

The analysis before AsyncCall# execute said, in the fourth row getResponseWithInterceptorrChain completed in the process of the whole network request, responsibility in the process chain calls the interceptor and network interceptor. The following is a source code analysis of the interceptor invocation process.

AsyncCall#getResponseWithInterceptorChain

Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of interceptors. List<Interceptor>  interceptors = new ArrayList<>(); interceptors.addAll(client.interceptors()); interceptors.add(retryAndFollowUpInterceptor); interceptors.add(new BridgeInterceptor(client.cookieJar())); interceptors.add(new CacheInterceptor(client.internalCache())); interceptors.add(new ConnectInterceptor(client));if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }
Copy the code

As you can see, all interceptors are placed in the interceptors list first. The interceptor is then invoked on lines 14 through 16. There is also a major analysis of what the interceptors provided by each system are responsible for during the network request process. (Not covered in this article)

RealInterceptorChain#proceed

  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
    ...

    // Call the next interceptor inthe chain. RealInterceptorChain next = new RealInterceptorChain( interceptors, streamAllocation, httpCodec, connection, index + 1, request); Interceptor interceptor = interceptors.get(index); Response response = interceptor.intercept(next); .return response;
  }
Copy the code

Index starts at 0, is called from the first interceptor, creates the next RealInterceptorChain in RealInterceptorChain# PROCEED, and executes the current interceptor.

Let’s pause here and see how an interceptor is defined.

public class CustomInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); . // Do what interceptors do...returnchain.proceed(request); }}Copy the code

The last line of the last statement that was with AsyncCall# getResponseWithInterceptorChain is the same. This creates a chain of interceptors executed one by one in index + 1 sequence, with each interceptor responsible for its own responsibility. This is the chain of responsibility pattern.

A synchronous request

OkHttpCall#execute

  @Override public Response<T> execute() throws IOException {
    okhttp3.Call call;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      if(creationFailure ! = null) {if (creationFailure instanceof IOException) {
          throw (IOException) creationFailure;
        } else {
          throw (RuntimeException) creationFailure;
        }
      }

      call = rawCall;
      if(call == null) { try { call = rawCall = createRawCall(); } catch (IOException | RuntimeException e) { creationFailure = e; throw e; }}}if (canceled) {
      call.cancel();
    }

    return parseResponse(call.execute());
  }
Copy the code

As in asynchronous request analysis, the main thing is to call the createRawCall method, which was analyzed above. It then goes to the execute method called in the OkHttpClient. Since it is a synchronous request, the result of the request is finally parsed in parseResponse and called back to the Android front end.

OkHttpClient#execute

  @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      returnresult; } finally { client.dispatcher().finished(this); }}Copy the code

Line 8, the entry into the source code is

  /** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }
Copy the code

Add RealCall to runningSyncCalls.

 /** Running synchronous calls. Includes canceled calls that haven't finished yet. */ private final Deque
      
        runningSyncCalls = new ArrayDeque<>();
      Copy the code

In line 9, the request is also called the getResponseWithInterceptorChain approach as a result, it has been said in the analysis of the asynchronous request.