preface

I recently came up with the idea of doing an in-depth analysis of the main Android open source framework, and then writing a series of articles, including detailed usage and source code analysis of the framework. The purpose is to understand the underlying principle of the framework by appreciating the source code of god, that is, to do not only know it, but also know why.

Here I say their own experience reading source code, I generally in accordance with the peacetime use of a framework or a system source code process, first of all to know how to use, and then go to the bottom of each step to do what, with what good design patterns, why so design.

Series of articles:

  • Android mainstream open source framework (a) OkHttp -HttpClient and HttpURLConnection use details
  • Android main open source framework (ii) OkHttp usage details
  • Android mainstream open source framework (three) OkHttp source code analysis
  • Android mainstream open source framework (iv) Retrofit usage details
  • Android mainstream open source framework (v) Retrofit source code analysis
  • Android mainstream open source framework (six) Glide execution process source code analysis
  • More frameworks continue to be updated…

Check out AndroidNotes for more dry stuff

A basic use example of Retrofit

1.1 Synchronization Request

(1) Create network request interface:

public interface PostmanService {
    @GET("get")
    Call<PostmanGetBean> testGet(a);
}
Copy the code

(2) Create an instance of Retrofit:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://postman-echo.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();
Copy the code

(3) Create an instance of the network request interface and Call the method in the interface to obtain the Call object:

PostmanService service = retrofit.create(PostmanService.class);
Call<PostmanGetBean> call = service.testGet();
Copy the code

(4) Make network requests

Response<PostmanGetBean> response = call.execute();
Copy the code

1.2 Asynchronous Request

The only difference between synchronous requests and asynchronous requests is step (4), which uses the synchronous method execute() and the asynchronous method enqueue(). Step (4) of the asynchronous request is as follows:

call.enqueue(new Callback<PostmanGetBean>() {
	@Override
	public void onResponse(Call<PostmanGetBean> call, Response<PostmanGetBean> response) {}@Override
	public void onFailure(Call<PostmanGetBean> call, Throwable t) {}});Copy the code

For more information on how to use Retrofit, see my previous article for a full explanation of how to use Retrofit.

Retrofit source code analysis

Source version: 2.5.0

2.1 (1) Create a network request interface

public interface PostmanService {
    @GET("get")
    Call<PostmanGetBean> testGet(a);
}
Copy the code

This step is relatively simple, just create an interface that contains an annotated method. There is no source code to talk about here, we will talk about it later with step (3).

2.2 (2) Creating an instance of Retrofit

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://postman-echo.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();
Copy the code

First we click on the Retrofit object and it has the following constants:

  /*Retrofit*/
  // The ServiceMethod cache is used to store network request-related configurations, such as network request methods, data converters, network request adapters, network request factories, and base addresses
  private finalMap<Method, ServiceMethod<? >> serviceMethodCache =new ConcurrentHashMap<>();
  // Network requester factory
  final okhttp3.Call.Factory callFactory;
  // Network request url
  final HttpUrl baseUrl;
  // A collection of data converter factories
  final List<Converter.Factory> converterFactories;
  // Collection of network request adapter factories
  final List<CallAdapter.Factory> callAdapterFactories;
  // Callback method executor
  final @Nullable Executor callbackExecutor;
  // A flag bit to determine whether the ServiceMethod needs to be loaded
  final boolean validateEagerly;
Copy the code

All of these constants, except serviceMethodCache, can be configured in builder mode, for example baseUrl can be configured in baseUrl().

Then look at the Builder() method:

    /*Retrofit-Builder*/
    Builder(Platform platform) {
      this.platform = platform;
    }
    public Builder(a) {
      this(Platform.get());
    }
Copy the code

Get (); findPlatform(); get(); findPlatform();

  /*Platform*/
  private static final Platform PLATFORM = findPlatform();

  static Platform get(a) {
    return PLATFORM;
  }

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

As you can see, the Builder() method is mainly used to find the platform currently running, and Retrofit supports Android and Java platforms.

Finally, look at the build() method:

    /*Retrofit*/
    public Retrofit build(a) {
      / / (1)
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
      / / (2)
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
      / / (3)
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }
      / / (4)
      // 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));
      // (5) star
      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories = new ArrayList<>(
          1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

      // 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);
      converterFactories.addAll(platform.defaultConverterFactories());
      // (5) end
      / / (6)
      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }
Copy the code

In the source code, I have identified six concerns, as follows:

  • (1) : Indicates that baseUrl must be set; otherwise, an exception will be reported.
  • (2) : If callFactory is not set, OkHttpClient is used by default.
  • (3) : If no callbackExecutor is set, the default callbackExecutor of the current running platform is used.

DefaultCallbackExecutor () looks like this:

  /*Platform*/
  @Nullable Executor defaultCallbackExecutor(a) {
    return null;
  }
Copy the code

DefaultCallbackExecutor (); defaultCallbackExecutor();

    /*Platform-Android*/
    @Override public Executor defaultCallbackExecutor(a) {
      return new MainThreadExecutor();
    }
Copy the code

Follow up to MainThreadExecutor() :

    /*Platform-Android*/
    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

As you can see, we’re going to switch the thread from the child thread to the main thread by creating a Handler for the main thread, so that we can get the data from the server from the main thread, and we can do the UI directly.

  • (4) : Configure the callAdapter. Factory collection by adding the client Settings to the collection. Then add the platform’s default (CompletableFutureCallAdapterFactory and ExecutorCallAdapterFactory).
  • (5) : To configure the Converter.Factory collection, first add BuiltInConverters to the collection, then add the client Settings (we set GsonConverterFactory earlier), Finally add the current platform default (OptionalConverterFactory).
  • (6) : Assign constants in Retrofit by calling its constructor.

Summary: This step uses the Builder pattern to create instances of Retrofit, During the creation process, the network requester factory (callFactory) and the URL of the network request are configured Address (baseUrl), collection of data converterFactories (converterFactories), collection of network request adapter factories (callAdapterFactories), and callbackExecutor. The advantage of using the Builder pattern here is that you don’t need to know how Retrofit is created internally, just pass in the corresponding configuration to create very complex objects.

2.3 (3) Create an instance of the network request interface and Call the method in the interface to get the Call object

PostmanService service = retrofit.create(PostmanService.class);
Call<PostmanGetBean> call = service.testGet();
Copy the code

First we click on the create() method and the source looks like this:

  /*Retrofit*/
  public <T> T create(final Class<T> service) {
    // Validate the incoming service (service must be an interface and cannot inherit from any other interface, otherwise an exception will be thrown)
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    // (1) proxy.newproxyInstance ()
    return (T) Proxy.newProxyInstance(service.getClassLoader(), newClass<? >[] { service },new InvocationHandler() {
          private final Platform platform = Platform.get();
          private final Object[] emptyArgs = new Object[0];
		  // (2) invoke()
          @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.
            / / (3)
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            / / (4)
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            / / (5)
            returnloadServiceMethod(method).invoke(args ! =null? args : emptyArgs); }}); }Copy the code
  • (1) : The dynamic proxy is used here to create an instance of the network request interface and return it. The parameters in the newProxyInstance() method are expressed as follows: service.getclassloader () : ClassLoader, the ClassLoader of the proxied object. New Class[] {service} : Class[], all the interfaces that the proxied object needs to implement. New InvocationHandler(){} : InvocationHandler, which implements the InvocationHandler interface.

Note that the newProxyInstance() method executing Proxy in concern (1) already creates an instance of the network request interface, and the call to create() method does not continue to execute the invoke() method in concern (2). The invoke() method is executed when a method in the network request interface is called (for example, service.testget ()).

Let’s move on to the second line of code in our example:

Call<PostmanGetBean> call = service.testGet();
Copy the code

Call testGet() to invoke the invoke() method in the dynamic proxy above, so let’s move on to other concerns.

  • (2) : The invoke() method is a method to be overridden by the InvocationHandler interface. This method will invoke the propied-object method through Java reflection mechanism. The parameters in the invoke() method are represented as follows: Proxy: a dynamic proxy object, in this case PostmanService. Method: method of the proxied object, in this case testGet(). Args: arguments to the propped object method, null here because testGet() does not pass arguments in the example.
  • (3) : If the method is an Object method, such as equals(), toString(), then call it directly.
  • (4) : if the method is the Java8 default method, the platform default method is executed.
  • (5) : If neither of the preceding two conditions is met, the loadServiceMethod() method is called to obtain ServiceMethod and then invoke().

Let’s start with the loadServiceMethod() method:

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

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = ServiceMethod.parseAnnotations(this, method); serviceMethodCache.put(method, result); }}return result;
  }
Copy the code

The ServiceMethod for the current method is first fetched from the ServiceMethod cache collection, or returned if it is not empty. If empty, a new ServiceMethod is created using the singleton pattern, called the parseAnnotations() method, and then stored in the cache collection.

Let’s click on the parseAnnotations() method to go there:

  /*ServiceMethod*/
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    / / (6)
    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.");
    }
	/ / (7)
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }
Copy the code
  • (6) : Call RequestFactory’s parseAnnotations() method to resolve annotations from the network request interface.

Take a look at the RequestFactory’s parseAnnotations() method:

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

The Builder mode is used here, and the Builder constructor is just a few simple assignments like this:

    /*RequestFactory-Builder*/
    Builder(Retrofit retrofit, Method method) {
      / / Retrofit object
      this.retrofit = retrofit;
      / / method
      this.method = method;
      // Method annotations
      this.methodAnnotations = method.getAnnotations();
      // Parameter type
      this.parameterTypes = method.getGenericParameterTypes();
      // Array of parameter annotations
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }
Copy the code

Move on to the build() method:

    /*RequestFactory-Builder*/
    RequestFactory build(a) {
      / / (6.1)
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
	  //...
      / / (6.2)
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = newParameterHandler<? >[parameterCount];for (int p = 0; p < parameterCount; p++) {
        parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p]);
      }
	  //...
	  / / (6.3)
      return new RequestFactory(this);
    }
Copy the code

As you can see, the for loop in concern (6.1) is used to parse annotations on methods, such as @GET, @HTTP, @headers, and so on. The for loop in concern (6.2) is used to parse the parameters on the method. Executing to concern (6.3) indicates that the annotation in the network request interface has been resolved, and then creates and assigns the RequestFactory object to return as follows:

  /*RequestFactory*/
  RequestFactory(Builder builder) {
    // method, in this case testGet()
    method = builder.method;
    // baseUrl, in this case https://postman-echo.com/
    baseUrl = builder.retrofit.baseUrl;
    // HTTP method, in this case a GET request
    httpMethod = builder.httpMethod;
    // Relative path, in this case get
    relativeUrl = builder.relativeUrl;
    // Request header, not set, default to null
    headers = builder.headers;
    // contentType, not set, defaults to null
    contentType = builder.contentType;
    // hasBody, not set, default is false
    hasBody = builder.hasBody;
    // isFormEncoded, unset, defaults to false
    isFormEncoded = builder.isFormEncoded;
    // isMultipart, not set, default is false
    isMultipart = builder.isMultipart;
    // Array of parameters. In this example, no parameters are passed, so null
    parameterHandlers = builder.parameterHandlers;
  }
Copy the code
  • To return to concern (7), click on the parseAnnotations() method in HttpServiceMethod:
  /*HttpServiceMethod*/
  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) {
    // Get the corresponding network request adapter according to the return type and annotation in the network request interface (CallAdapter)
    CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method);
    // Get the response type, in this case PostmanGetBean
    Type responseType = callAdapter.responseType();
    if (responseType == Response.class || responseType == okhttp3.Response.class) {
      throw methodError(method, "'"
          + Utils.getRawType(responseType).getName()
          + "' is not a valid response body type. Did you mean ResponseBody?");
    }
    if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
      throw methodError(method, "HEAD method must use Void as response type.");
    }
    // Get the corresponding data Converter based on the return type and annotation in the network request interface (Converter)
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);
    // Get the network requester Factory (okHttp3.call.factory)
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter);
  }
Copy the code

As you can see, this method is mainly used to obtain the corresponding network request adapter (CallAdapter), data Converter (Converter) and network requester Factory (okHttp3.call.factory). Corresponding case were ExecutorCallAdapterFactory, GsonConverterFactory OkHttpClient. Finally, we use this to create an HttpServiceMethod object and return it. HttpServiceMethod inherits from ServiceMethod, so it can be converted to ServiceMethod.

The loadServiceMethod() method takes the ServiceMethod object and goes back to the rest of concern (5) :

/*Retrofit*/
returnloadServiceMethod(method).invoke(args ! =null ? args : emptyArgs);
Copy the code

The invoke() method is invoked and the invoke() method of ServiceMethod is clicked in and implemented in a subclass of HttpServiceMethod as follows:

  /*ServiceMethod*/
  abstract T invoke(Object[] args);
  
  /*HttpServiceMethod*/
  @Override ReturnT invoke(Object[] args) {
    return callAdapter.adapt(
        new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
  }
Copy the code

As you can see, the CallAdapter adapt() method is called, and the argument is to create an OkHttpCall object. The creation of the OkHttpCall object is just a simple assignment. Has been said above, corresponding example is CallAdapter ExecutorCallAdapterFactory, so click to adapt () method to check:

    /*ExecutorCallAdapterFactory*/
    @Nullable
    publicCallAdapter<? ,? > get(Type returnType, Annotation[] annotations, Retrofit retrofit) {if(getRawType(returnType) ! = Call.class) {return null;
        } else {
            final Type responseType = Utils.getCallResponseType(returnType);
            return newCallAdapter<Object, Call<? > > () {public Type responseType(a) {
                    return responseType;
                }
				/ / concerns
                public Call<Object> adapt(Call<Object> call) {
                    return new ExecutorCallAdapterFactory.ExecutorCallbackCall(ExecutorCallAdapterFactory.this.callbackExecutor, call); }}; }}Copy the code

As you can see from the focus, the adapt() method returns an ExecutorCallbackCall, which implements the Call interface and thus fetches the Call object, You can happily call the execute() and enqueue() methods to make network requests.

Summary:

  1. The dynamic proxy pattern is first used to create an instance of the network request interface (in this case, PostmanService) by calling Retrofit.create ().
  2. The invoke() method in the dynamic proxy is then executed by calling a method in the network request interface (in this case, service.testget ()).
  3. The invoke() method uses singleton and builder patterns to create ServiceMethod objects that are fetched directly from the cache if created and recreated if not. The re-creation process parses annotations, parameters, return types, and so on on methods in the web request interface into the parameters required for HTTP requests.
  4. Finally, the Call object required by the network request is retrieved by calling the invoke() method in the ServiceMethod object.

The advantage of using dynamic proxies here is that all calls to the network request interface’s methods can be centrally forwarded to the Invoke () method of the InvocationHandler interface for centralized processing.

2.4 (4) Making network requests

Network request can be divided into synchronous request and asynchronous request. The following describes the two request modes.

2.4.1 Synchronizing Requests

Response<PostmanGetBean> response = call.execute();
Copy the code

Call is an ExecutorCallbackCall interface. Click execute() to check out the call interface:

    /*ExecutorCallAdapterFactory-ExecutorCallbackCall*/
    @Override public Response<T> execute(a) throws IOException {
      return delegate.execute();
    }
Copy the code

The delegate here is actually OkHttpCall, so click on the execute() method here to check it out:

  /*OkHttpCall*/
  @Override public Response<T> execute(a) 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 if (creationFailure instanceof RuntimeException) {
          throw (RuntimeException) creationFailure;
        } else {
          throw (Error) creationFailure;
        }
      }

      call = rawCall;
      if (call == null) {
        try {
          / / (1)
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException | Error e) {
          throwIfFatal(e); // Do not assign a fatal error to creationFailure.
          creationFailure = e;
          throwe; }}}if (canceled) {
      call.cancel();
    }
    / / (2)
    return parseResponse(call.execute());
  }
Copy the code
  • Create a Call of type okHttp3. Call. Click createRawCall() to see how it is created:
  /*OkHttpCall*/
  private okhttp3.Call createRawCall(a) 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

As you can see, a Request object is built using requestFactory.create(args) and then the call is created using OkHttpClient’s newCall() method. How newCall() creates a call is described in this section. The OkHttp source code is used to parse Android’s main open source framework.

Let’s continue with the create() method:

  /*RequestFactory*/
  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);

    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

As you can see, the constants such as httpMethod, baseUrl, relativeUrl, and headers are derived from parsing the web Request interface annotations in step (3) to construct a Request.

  • Execute () is the OkHttp method. Execute () is the OkHttp method. This is about Android mainstream open source framework (three) OkHttp source code parsing has talked about, here will not expand to tell.

The next step is to parse the data returned by the response. Click the parseResponse() method to see:

  /*OkHttpCall*/
  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 {
      / / (2.1)
      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; }}Copy the code

Without further discussion of status code checks, let’s move to concern 2.1. Yes, this is returned by parsing the response data into the corresponding entity class through the Converter we set up. Let’s look at the convert() method:

  /*GsonResponseBodyConverter*/
  @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      T result = adapter.read(jsonReader);
      if(jsonReader.peek() ! = JsonToken.END_DOCUMENT) {throw new JsonIOException("JSON document was not fully consumed.");
      }
      return result;
    } finally{ value.close(); }}Copy the code

Simply, the response data is parsed through Gson into the corresponding entity class returned.

2.4.2 Asynchronous Request

call.enqueue(new Callback<PostmanGetBean>() {
	@Override
	public void onResponse(Call<PostmanGetBean> call, Response<PostmanGetBean> response) {}@Override
	public void onFailure(Call<PostmanGetBean> call, Throwable t) {}});Copy the code

Click the enqueue() method to check it out:

    /*ExecutorCallAdapterFactory-ExecutorCallbackCall*/
    @Override public void enqueue(final Callback<T> callback) {
      checkNotNull(callback, "callback == null");
      / / (1)
      delegate.enqueue(new Callback<T>() {
        / / (2)
        @Override public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run(a) {
              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); }}}); }/ / (3)
        @Override public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run(a) {
              callback.onFailure(ExecutorCallbackCall.this, t); }}); }}); }Copy the code
  • (1) : The delegate here is actually OkHttpCall, click on the enqueue() method here to check it out:
  /*OkHttpCall*/
  @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 {
          / / (1.1)
          call = rawCall = createRawCall();
        } catch(Throwable t) { throwIfFatal(t); failure = creationFailure = t; }}}if(failure ! =null) {
      callback.onFailure(this, failure);
      return;
    }

    if (canceled) {
      call.cancel();
    }
    / / (1.2)
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
          / / (1.3)
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          throwIfFatal(e);
          callFailure(e);
          return;
        }

        try {
          / / (1.4)
          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 {
          / / (1.5)
          callback.onFailure(OkHttpCall.this, e);
        } catch(Throwable t) { t.printStackTrace(); }}}); }Copy the code
  • (1.1) : The createRawCall() method is no longer expanded, as is the case with synchronous requests.
  • (1.2) : As with synchronous requests, the call is of type okHttp3. call, so enqueue() is the method in OkHttp, so the underlying network requests are actually made through OkHttp. This is about Android mainstream open source framework (three) OkHttp source code parsing has talked about, here will not expand to tell.
  • (1.3) : The parseResponse() method is no longer expanded, as is the case with synchronous requests.
  • (1.4) : Network request successful, callback to concern (2).
  • (1.5) : Network request failed, callback to concern (3).
  • (2) : Calls back onResponse() to the UI thread and then to the onResponse() method in our example.
  • (3) : Call onFailure() back to the UI thread and then call back to the onFailure() method in our example.

Summary: The synchronous and asynchronous requests in this step actually call OkHttp’s synchronous and asynchronous request methods respectively, and then parse the data into the corresponding entity class through the data Converter we set up. Synchronous requests are returned directly, and asynchronous requests are switched to the main thread and returned.

Third, summary

  • (1) Create network request interface:

This step is to configure various parameters required by HTTP requests, such as HTTP request methods, request headers, request parameters, and request addresses.

  • (2) Create an instance of Retrofit:

This step is to create an instance of Retrofit through the Builder pattern, During the creation process, the network requester factory (callFactory) and the URL of the network request are configured Address (baseUrl), collection of data converterFactories (converterFactories), collection of network request adapter factories (callAdapterFactories), and callbackExecutor. The advantage of using the Builder pattern here is that you don’t need to know how Retrofit is created internally, just pass in the corresponding configuration to create very complex objects.

  • (3) Create an instance of the network request interface and Call the method in the interface to obtain the Call object:

This step creates an instance of the network request interface through the dynamic proxy pattern, and then invokes the invoke() method in the dynamic proxy by calling the method in the network request interface. This step is mainly used to parse annotations, parameters, and return types of methods in the network request interface, that is, to parse methods in the network request interface into various parameters required by HTTP requests. The advantage of using dynamic proxies here is that all calls to the network request interface’s methods can be centrally forwarded to the Invoke () method of the InvocationHandler interface for centralized processing.

  • (4) Network request:

The synchronous and asynchronous requests in this step actually call OkHttp’s synchronous and asynchronous request methods respectively, and then parse the data into the corresponding entity class through the data Converter we set up. Synchronous requests are returned directly, and asynchronous requests are switched to the main thread and returned.

References:

  • Android: Take you through Retrofit 2.0 source code hand by hand
  • Dismantle the Wheel Series: Dismantle Retrofit

About me

I am Wildma, CSDN certified blog expert, excellent author of Simple book programmer, good at screen adaptation. If the article is helpful to you, a “like” is the biggest recognition for me!