Have you ever wondered after using Retrofit:

1. Why does Retrofit stand out when there is such a powerful web framework as OkHttp? 2. How does Retrofit fit third-party frameworks? What design patterns did Retrofit use? Why is Retrofit easy to use?

Let’s find out what Retrofit really looks like.

This article is based on the following Java version parsing:

// retrofit
implementation 'com. Squareup. Retrofit2: retrofit: 2.5.0'  
implementation 'com. Squareup. Retrofit2: converter - gson: 2.0.2'  
implementation 'com. Squareup. Retrofit2: adapter - rxjava2:2.3.0'

// rxjava
implementation 'the IO. Reactivex. Rxjava2: rxjava: 2.1.6'
implementation 'the IO. Reactivex. Rxjava2: rxandroid: 2.0.1'
Copy the code

Contents of this article:

  • 1. The significance of The existence of Retrofit
  • 2. Use of Retrofit
  • 3. Create Retrofit
  • Create a Retrofit.Builder
  • The core of Retrofit, dynamic proxies
  • Dynamic proxy for Retrofit
  • 7,

1. The significance of The existence of Retrofit

Retrofit is not a pure web framework, why is that? Retrofit is based on the OkHttp framework to achieve the request, and Retrofit is based on the OkHttp framework to achieve a set of encapsulation, the use of dynamic proxy to achieve a simple network request implementation, and support Gson, RxJava and other third-party framework adaptation, in short, is to make your network request more convenient, more powerful;

So what exactly is the underlying encapsulation of Retrofit? How is it more convenient, more powerful?

Next, let’s go deep into the source code and take a look!

2. Use of Retrofit

Light source, do not speak about the use, always feel a little overhead, easy to find the point;

So let’s take a look at the simple use of Retrofit;

Retrofit interface:

public interface GetRequestInterface {

    @GET("openapi.do? Keyfrom = Yanzhikai&key = 2032414398 & type = data&doctype = json&version = 1.1 & q = car ")
    Call<ResultData> getCall(a);

    @GET("openapi.do? Keyfrom = Yanzhikai&key = 2032414398 & type = data&doctype = json&version = 1.1 & q = car ")
    Observable<ResultData> getObservableCall(a);

    @GET("openapi.do? Keyfrom = Yanzhikai&key = 2032414398 & type = data&doctype = json&version = 1.1 & q = car ")
    Call<StudentBean> getItem(@Query("student") String student,  @Query("type") String type);
}
Copy the code

Base request URL:

public class BaseRequest {

    public static final String BaseURL = "https://fanyi.youdao.com/";

}
Copy the code

Use of Retrofit:

        // Create a Retrofit instance
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BaseRequest.BaseURL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();

        // Create the request interface class
        GetRequestInterface request = retrofit.create(GetRequestInterface.class);
    	// -- common network request
        // Get the request object Call
        Call<ResponseBody> call = request.getCall();
        // Execute the network request
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                ResponseBody body = response.body();
                Log.i("TAG"."MainActivity onResponse response:" + response.toString());
                Log.i("TAG"."MainActivity onResponse body:" + (body == null ? "null" : body.toString()));
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                Log.i("TAG"."MainActivity onFailure t:"+ t.toString()); }});// --RxJava Network request
        Observable<ResultData> observableCall = request.getObservableCall();

        observableCall.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<ResultData>() {
                    @Override
                    public void onSubscribe(Disposable d) {}@Override
                    public void onNext(ResultData resultData) {
                        Log.i("TAG"."retrofit onNext resultData:" + resultData.toString());

                    }

                    @Override
                    public void onError(Throwable e) {}@Override
                    public void onComplete(a) {
                        Log.i("TAG"."retrofit onComplete "); }});Copy the code

There are several steps:

(1) Create a Retrofit instance through the constructor pattern; (2) Create an instance of the interface through dynamic proxy; (3) Obtain the operation class Call of the network request through the instance of the interface; (4) Executing network requests through Call;

Take a look at the general flow chart:

Let’s dive into the framework based on usage and explore the subtleties of its source code!

3. Create Retrofit

3.1. Variables of Retrofit

        // Create a Retrofit instance
        Retrofit retrofit = new Retrofit.Builder()
        		/ / create baseUrl
                .baseUrl(BaseRequest.BaseURL)
                / / add GsonConverterFactory
                .addConverterFactory(GsonConverterFactory.create())
                // Add RxJavaCallAdapterFactory to RxJava
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                // Build an instance of Retrofit
                .build();
Copy the code

Retrofit is built using the builder pattern, which has the advantage of being able to construct complex objects, extend them easily, and look clean and beautiful.

Before we get started, let’s take a look at the Retrofit member variables;

There’s not a lot of variables here, so let’s go through them one by one;

** (1) Map

> serviceMethodCache: ** This is a cache class for methods. Key is a Method for network requests, such as GET, POST, etc. ** (2) okhttp3.call.factory callFactory: ** this is the Factory class that creates OkHttp; ** (3) HttpUrl baseUrl: ** this is the baseUrl that the network request will carry; ** (4) List< converter.factory > converterFactories: ** ConverterFactory is a collection of converters.Factory is a Factory that converts the returned data to the corresponding data, such as Gson’s GsonConverterFactory class, which is the data ConverterFactory; ** (5) List< callAdapter.factory > callAdapterFactories: ** Set of callAdapter.factory. Calladapter.factory is an adapter Factory for network requests, such as the RxJavaCallAdapterFactory Factory that converts calls to RxJava requests. ** (6) Executor callbackExecutor: ** is used to callback network requests; ** (7) Boolean validatezh: ** is used to determine whether immediate parsing methods are needed, which we’ll discuss when we talk about dynamic proxies;
,>

We’ve looked at the member variables of a Retrofit, but the Builder is used to create a Retrofit. Let’s take a look at the variables of a Retrofit Builder and see what they do.

Create a Retrofit.Builder

4.1 variables for Retrofit.Builder

The image shows that the variables are similar to Retrofit, the only difference is that they have Platform and validate15, so let’s see what these two variables do.

Take a look at the value assigned to the Platform;

The Platform is fetched by the platform.get () method. Let’s see what the logic is;

As you can see from the image, the findPlatform() method is finally called;

This method returns the Android, Java, Platform, and other classes. It is used to determine which Platform to use, and then to obtain the Platform to use.

private static Platform findPlatform(a) {
    try {
      Class.forName("android.os.Build");
      // If the SDK obtained from the system is not 0, it is the Android platform;
      if(Build.VERSION.SDK_INT ! =0) {
        return newAndroid(); }}catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      // If it is not an Android platform, return to the Java platform;
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    // The default return value;
    return new Platform();
  }
Copy the code

Build.version. SDK_INT ();

Translated, it means:

The SDK version of the software currently running on this hardware. This value does not change when the device is booted, but may change when the hardware manufacturer provides OTA updates;

Let’s take a look at the source code for this Android class:

There are only a few methods in the source code of the Android class. Here we will focus on the following methods:

** (1) defaultCallbackExecutor() : ** defaultCallbackExecutor() : **

* * (2) defaultCallAdapterFactories: * * to get the default CallAdapter in Factory, used to create CallAdapter;

* * (3) defaultConverterFactories: * * to get the default data transformation the factory ConverterFactory, the Converter is used to create Converter;

The default Executor class is CallAdapterFactory, the data conversion factory class is ConverterFactory, and the data conversion factory class is ConverterFactory.

4.2, baseUrl

BaseUrl, which is the baseUrl for our web requests;

Let’s go into the source code to see what the specific operation;

The first step is to parse the baseUrl and return an HttpUrl object. 2, the first step is to create an HttpUrl object to the Builder;

HttpUrl’s parse(@Nullable HttpUrl Base, String Input) will parse the URL to determine whether the request’s scheme is HTTP or HTTPS. If it’s not one of these, it throws an exception, and we’ll take a look at the source code;

Then the following will parse the URL, get the host address host, port number port, specific source CODE I will not stick, interested can follow the source code to see;

4.3, the Converter. The Factory

This is the factory used to create the Converter, using the abstract factory design pattern. The Converter is used to convert the data returned by the request into the data of the corresponding platform. In this case, we are using the Gson platform.

Let’s first take a look at the Converter.Factory to see how it is implemented.

Factory is the inner class of the Converter. There are two main methods: requestBodyConverter, which convert the RequestBody of a request to the corresponding Converter.

The other method is the responseBodyConverter, which converts the returned ResponseBody to the corresponding Converter, the Converter.

There is only one method, convert, which is used to convert the returned data to the corresponding type.

We are creating an example of Retrofit, is through GsonConverterFactory. The create () to create the corresponding converter plant, let’s look at is how to realize the Gson converter plant;

Let’s look at the create() method;

I ended up going over here, doing a simple assignment;

The most important part of the GsonConverterFactory is the responseBodyConverter and requestBodyConverter methods.

  • RequestBodyConverter:

Inside the requestBodyConverter method, we create Gson’s TypeAdapter with the class Type. This is a familiar TypeAdapter, which is the class we use to parse with Gson. Finally, the GsonRequestBodyConverter object is created using the TypeAdapter and gson parameters. Take a look at the code for this class;

The source code for this class is very simple. We mainly convert() this method;

The logic of this method is to convert the value passed in to a ByteString through the TypeAdapter, and then pass in the RequestBody as an argument to build the RequestBody object;

We saw the method here first, and we’ll see it later in the requestBodyConverter call;

  • ResponseBodyConverter:

This method is similar to the requestBodyConverter method above, which also creates the Gson TypeAdapter with the class Type. By TypeAdapter and eventually created GsonResponseBodyConverter gson parameters, in the same way, we also look at the implementation of this class;

The source code is simple, and here we also focus on the convert() method;

The logic here is familiar, but we often use gSON parsing, the TypeAdapter reads the data from the JsonReader, returns the corresponding data type, The ResponseBody parameter is generated by the convert method of our GsonRequestBodyConverter;

That’s enough for GsonConverterFactory, and we’ll talk more about it later where we need it;

4.5, CallAdapter. Factory

Calladapter.factory, as you can see from the name, is the Factory class used to create CallAdapter, which uses the abstract Factory design pattern. CallAdapter is used to convert the Call to the required request type, such as RxJava.

Adapt is a method of the interface, and it is given to subclasses to implement the logic of this method. We will parse Retrofit in the following, and then explain the logic of this method, here is to understand that this is a method of conversion.

Let’s take a look at what methods are used in the factory that creates the CallAdapter and what they do.

This Factory has very little logic, just a few methods, and we’re going to focus on the get method, which is the way to get the CallAdapter object, and again, we’re going to give it to the subclasses;

While we Retrifit created above, in CallAdapter. Factory to add, use RxJava Factory, namely RxJava2CallAdapterFactory, used to Call request converted to the corresponding request RxJava;

Let’s look at this RxJava2CallAdapterFactory implementation logic;

RxJava2CallAdapterFactory creation, but also through RxJava2CallAdapterFactory. The create () method, then we’ll look at the create methods to do what?

Simply new a RxJava2CallAdapterFactory, and no other logic in the constructor, only to the assignment of the Scheduler, which created, travels is null;

After we finish see RxJava2CallAdapterFactory created above, let’s take a look at how RxJava2CallAdapterFactory through the get method to create a CallAdapter;

publicCallAdapter<? ,? > get(Type returnType, Annotation[] annotations, Retrofit retrofit) { Class<? > rawType = getRawType(returnType);if (rawType == Completable.class) {
      / / create RxJava2CallAdapter
      return new RxJava2CallAdapter(Void.class, scheduler, isAsync, false.true.false.false.false.true); }.../ / create RxJava2CallAdapter
    return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult, isBody, isFlowable,
        isSingle, isMaybe, false);
  }
Copy the code

This method is responsible for creating an RxJava CallAdpter based on various parameters, namely RxJava2CallAdapter;

Let’s focus here on the logic of an RxJava2CallAdapte’s adapt method;

public Object adapt(Call<R> call) {
	// Step 1: Create an Observable based on the async parameter
    Observable<Response<R>> responseObservable = isAsync
        ? new CallEnqueueObservable<>(call)
        : newCallExecuteObservable<>(call); Observable<? > observable;// Step 2: Encapsulate another Observable return layer based on each judgment
    if (isResult) {
      observable = new ResultObservable<>(responseObservable);
    } else if (isBody) {
      observable = new BodyObservable<>(responseObservable);
    } else{ observable = responseObservable; }...return observable;
  }
Copy the code

This method converts a Call request into an RxJava request and returns an RxJava observer, an Observable, for making an RxJava type network request, as shown in the example above.

There are two main steps in the logic of this method. Let’s take a look at the observed that we created in the first step. We’ll determine whether it’s asynchronous or not.

The difference between the two is that when a subscribe is called, the subscribeActual method of CallEnqueueObservable is executed, and finally the asynchronous request is executed by the enqueue method of OkHttpCall.

When a CallExecuteObservable calls a subscribe, it also executes the CallEnqueueObservable subscribeActual method, and in this method, Invoke the execute method of OkHttpCall directly to execute the synchronous request;

The second step encapsulates BodyObservable, which encapsulates the subscribed observer and returns the body in the onNext method. This step can be understood as processing the returned result;

Here’s how to use RxJava, and by default you know how to use RxJava, but if you’re not familiar with RxJava, you can go back and see how to use RxJava;

With that out of the way for Retrofit’s calladapter.factory logic, let’s take a look at the logic for Retrofit’s final build() method;

4.5, Retrofit. Builder# build

No more nonsense, we direct source code;

public Retrofit build(a) {
      // If callFactory (OkHttpClient) is null, create a new OkHttpClient;
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

	  // When the Executor is null, the default Executor is assigned to the Platform.
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Create a new calladapter.factory collection by adding calladapter.factory;
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
     
// Add the default calladapter.factory for the Platform. If we don't add the calladapter.factory, we will use the default calladapter.factory for the Platform. callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

      // Create a collection of converter.factory
      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.
      // Add the default Converter.Factory
      converterFactories.add(new BuiltInConverters());
      // Add a custom Converter.Factory
      converterFactories.addAll(this.converterFactories);
      // Add the default Converter.Factory for Platform
      converterFactories.addAll(platform.defaultConverterFactories());
	  // Finally create a Retrofit instance;
      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }
Copy the code

The build() method, which is used to assign various parameters, eventually creates an instance of a Retrofit using the parameters.

Why is it that we can define a type through an interface to perform a request, and where is the operation for parsing these methods and assigning parameters?

This brings us to an important design pattern that Retrofit uses, the dynamic proxy design pattern;

The core of Retrofit, dynamic proxies

5.1 What is an agent?

For example, if I want to go to the supermarket to buy fruit, but I am very lazy, on the weekend I want to stay at home do not want to go out, but the heart is very want to eat fruit, how to do?

I can only open the takeaway App, and after buying on it, the takeaway boy will deliver it to me. At this time, I will buy the fruit through the intermediary, the takeaway App, and this process is called the agent;

Do not directly operate, but entrust a third party to operate, so as to achieve the purpose;

The Java proxy is divided into static proxy and dynamic proxy;

5.2. What is static Proxy?

If the proxy class existed before the program was run, the proxy is called static.

As in the example above, I want to buy fruit.

public interface Operator {
	/ / to buy fruit
	void buyFruit(a);
}
Copy the code

Meanwhile, we need to implement this interface for our agent-based takeout App. At the same time, we will pass in the objects that need to be entrusted. In the process of buyFruit, we will do some things, such as go to the supermarket to pick up the goods.

public class AppAgent implements Operator {
    
    private Operator operator;

    public AppAgent(Operator operator) {
        this.operator = operator;
    }

    @Override
    public void buyFruit(a) {
    	// 1. Provide products for users to place orders on the App
        
        // 2. Go to the supermarket to buy fruit according to the order
        operator.buyFruit();
        
        // 3. Delivery to customer}}Copy the code

Entrusted object, supermarket:

public class Market implements Operator {

    @Override
    public void buyFruit(a) {
		// Go to the supermarket to buy fruit}}Copy the code

So the final effect is that we bought fruit from the supermarket through a meal operation of the takeaway App, as follows:

public class Main {

    public static void main(String[] args) {

        // Delegate object, supermarket;
        Market market = new Market();
        // Proxy object, takeaway App;
        AppAgent appAgent = new AppAgent(market);
        // I bought the fruit from the supermarket through the agent of the takeaway App;appAgent.buyFruit(); }}Copy the code

The above is the process of our static agency. This example only shows the process of buying fruit. But what if I want to buy vegetables, daily necessities and a series of things?

I have to add several more methods to the Operator interface, and the same proxy class has to override a bunch of methods that do the same thing: buy the action, but we have to add a new type, and we have to override and call it in the proxy class.

So this process can actually be extracted, and this is called dynamic proxy;

5.3 dynamic proxy

Dynamic proxies, unlike static proxies, are created after they are run, whereas static proxies exist before they are run.

To put it plainly, and static proxy is different, dynamic proxy methods are in the run, automatically generated, so called dynamic proxy;

Let’s see how dynamic proxies work;

The invoke method is called from the dynamically generated proxy class, which corresponds to the static proxy operator.buyFruit() method we called above. Let’s take a look at the parameters of this method;

public interface InvocationHandler { 
  // Object proxy: Object proxy class;
  // Method: automatically generate Method after parsing;
  // Object[] args: parameter for the method;
  Object invoke(Object proxy, Method method, Object[] args);
}
Copy the code

Proxy0, Proxy0, Proxy0, Proxy1, etc., will be generated by the proxy.newProxyInstance () method, which will be covered in the following section.

Public final class $Proxy0 extends Proxy implements Operator {public final Boolean buyFruit() {// h is InvocationHandlel, The invoke method is called; super.h.invoke(this, m1, (Object[])null); }}}Copy the code

In the generated proxy class, we will implement our interface, override the method, invoke the invoke method using the InvocationHandler parameter from the method, and finally invoke the method of the proxy object through reflection.

Invoke (object, args); invoke(object, args); invoke(object, args); Then we can do some processing before or after the method. Invoke (object, args), so that all methods can be processed at the same time, rather than having to override the logic every time we add a method.

Here’s a look at the implementation:

public class CustomProxy implements InvocationHandler {

    private Object object;

    public CustomProxy(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1. Provide products for users to place orders on the App
        // doSomeThing();

        // 2. Go to the supermarket according to the order (before or after this method can be unified processing operation)
        Object invoke = method.invoke(object, args);

        // 3. Delivery to customer
        // doSomeThing();
        returninvoke; }}Copy the code

So let’s look at the final call;

public class Main {

    public static void main(String[] args) {
        // Create the proxied class
        CustomProxy customProxy = new CustomProxy(new Market());
        // Generate proxy classes dynamically
        Operator proxyInstance = (Operator) Proxy.newProxyInstance(Operator.class.getClassLoader(), new Class[]{Operator.class}, customProxy);
        // Call the corresponding methodproxyInstance.buyFruit(); }}Copy the code

Proxy.newproxyinstance () is used to generate Proxy class $Proxy0 dynamically.

Then the dynamic proxy class calls the method, and finally goes to the Invoke () method of the CustomProxy. Then we make the final proxy call through method.invoke(object, args). Before and after the final proxy call, we can implement custom logic;

This CustomProxy, which implements the InvocationHandler interface, acts more like an interception class, intercepting the invocation of the proxy method, and then implementing our custom logic.

So far, do you understand dynamic proxy? It doesn’t matter if you don’t understand, just watch it several times and practice it several times.

Dynamic proxy for Retrofit

Why should Retrofit use dynamic proxies?

First, let’s think about why Retrofit should use dynamic proxies.

The advantage of using dynamic proxies is that we can do all the operations before calling a method, rather than having to write the logic for a new method.

Retrofit uses dynamic proxies to resolve headers and urls before calling methods on the interface.

This way you don’t have to write the parsing logic every time you add a new request method;

Let’s take a look at how Retrofit parses these interfaces;

How does Retrofit use dynamic proxies?

Let’s take a look at the create method of Retrofit, where the dynamic proxy logic is implemented;

public <T> T create(final Class<T> service) {...// Verify that the interface parameters and configurations are correct
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    // Dynamic proxy
    return (T) Proxy.newProxyInstance(service.getClassLoader(), newClass<? >[] { service },new InvocationHandler() {
          private final Platform platform = Platform.get();
          private final Object[] emptyArgs = new Object[0];

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // Check whether it is an Object, and if it is, call the method directly
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            // Check whether it is the default method type of the Java8 platform. If it is, call the invokeDefaultMethod method of the Java8 platform
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            // Parse method;
            returnloadServiceMethod(method).invoke(args ! =null? args : emptyArgs); }}); }Copy the code

Here we divide the method into two steps;

** First step: ** eagerlyValidateMethods method. This method is used to load the configuration of the interface. It is used to determine whether the configuration of the interface’s header, body and method parameters are correct.

** Second step: ** loadServiceMethod method. This method’s logic is used to parse the annotations and parameters that we configured on the interface, such as header, body, URL, etc.

Here we focus on the loadServiceMethod method in step 2;

Let’s look at a concrete implementation of the source code;

ServiceMethod<? > loadServiceMethod(Method method) {// Get the ServiceMethod from the cache map;ServiceMethod<? > result = serviceMethodCache.get(method);if(result ! =null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        // If the ServiceMethod object is not obtained from the cache map, then parse the annotation to obtain the ServiceMethod object.
        result = ServiceMethod.parseAnnotations(this, method);
        // Store the parsed ServiceMethod object in the Map collection;serviceMethodCache.put(method, result); }}return result;
  }
Copy the code

The operation here is very simple, is to obtain the ServiceMethod, and in the process of obtaining the ServiceMethod, will first get it from the cache map, if can not be obtained, then parse, so that you do not need to obtain the ServiceMethod once, to parse, more performance.

The ServiceMethod class is abstract, with only two methods: a static parseAnnotations method and an abstract Invoke method.

Let’s take a look at this parseAnnotations method;

static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
	// Obtain the RequestFactory by parsing the interface method annotationsRequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); .// Parse the annotation and get the ServiceMethod object
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }
Copy the code

The logic of this method is not very complicated, it is divided into two steps;

Step 1: Obtain the RequestFactory; Step 2: Get the ServiceMethod.

Let’s look at the first step;

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

Create a RequestFactory from the Builder and see what the Builder does.

Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      // Get all the annotations for the method, both declared and inherited
      this.methodAnnotations = method.getAnnotations();
      // Get all types of method parameters, including generics;
      this.parameterTypes = method.getGenericParameterTypes();
      // Get all the annotations on the method parameters
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }
Copy the code

The logic of this method is simple. It is to do some assignment. The important thing to note here is that these reflected methods are used in the build method below;

RequestFactory build(a) {
      for (Annotation annotation : methodAnnotations) {
        // Iterate over the annotation of the method, parse the parameter configuration of the method, and get the url, header and other parameters of the requestparseMethodAnnotation(annotation); }...int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = newParameterHandler<? >[parameterCount];for (int p = 0; p < parameterCount; p++) {
        // Iterate over the parameters of the method, and their types, and parse the parameter logic of the methodparameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p]); }...// Create a RequestFactory based on the parameter configuration resolved above
      return new RequestFactory(this);
    }
Copy the code

The logic of this method is quite important. This is where the url, header, and other annotations that we define in the interface method are finally parsed and converted into the Call of the OKHTTP request.

Take a look at the parseMethodAnnotation method. The main logic for this method is to parse the configuration information for the method annotation.

Let’s take a look at the implementation of this class;

There’s a lot of logic to this method, so we’ll just take a look at it, but the main thing that’s done here is take the Annotation, take the URL, the header, the isMultipart, and assign it to the Builder, to the Builder member variable;

For this method, here is not much to say, interested to follow the source to have a look;

ParseParameter while the second method, also is the traversal access to the above method parameterTypes parameter types and parameters of annotations parameterAnnotationsArray, to resolve and obtain the relevant configuration, And this method is ultimately the logic of the modulated parseParameterAnnotation method;

It is mainly used to parse the logic here:

Let’s look at the implementation;

The parseParameterAnnotation method uses logic similar to the parseMethodAnnotation method above. It also parses by determining the type of the object.

Here we look for the parse of one of the queries to analyze;

Omit the previous code...else if (annotation instanceof Query) {
        ...
        // Convert the annotation to Query
        Query query = (Query) annotation;
        // Get the value corresponding to the Query annotation;String name = query.value(); .if (Iterable.class.isAssignableFrom(rawParameterType)) {
          // Make sure the parameter is of type Class;.// Create Query subclass of ParameterHandler
          return newParameterHandler.Query<>(name, converter, encoded).iterable(); }... }... Omit the following codeCopy the code

Query: ParameterHandler () ¶ Query: ParameterHandler (); Query: ParameterHandler (); Query: ParameterHandler ();

And that’s all we need to know;

Another important point to note here is that the parse involves the logic of the Convert method;

The convert method is used in the apply method of the Body class to convert the Body parameter to the corresponding data type.

The apply method is called when a network request is made. The specific invocation chain is as follows. Here, take RxJava as an example:

1, RxJavaCallAdapterFactory. ResponseCallAdapter adapt method; 2, RxJavaCallAdapterFactory. CallOnSubscribe call method; 3, RxJavaCallAdapterFactory. RequestArbiter request method; 4, OkHttpCall execute method; 5, OkHttpCall createRawCall method; Create RequestFactory (); 7, ParameterHandler apply method;

Finally here triggered the apply method call, here the source code is not posted, interested friends, can follow the source code to see again;

We’re looking at the RequestFactory build method, and we’re looking at the RequestFactory build method, and we’re looking at the RequestFactory build method, and we’re looking at the RequestFactory build method.

We’ve finished the first step of parseAnnotations. Let’s summarize this step by parsing the method annotations and method parameter annotations, obtaining the corresponding parameter configuration, and finally assigning it to the RequestFactory object.

Let’s take a look at parseAnnotations for HttpServiceMethod;

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) {
     / / create CallAdapterCallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method); ./ / create the Converter
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

	// Create HttpServiceMethod with parameters
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter);
  }
Copy the code

The logic of this method is not complicated. It consists of three main steps:

Step 1: Create CallAdapter; Step 2: Create the Converter; Step 3: Create HttpServiceMethod with parameters;

The main focus here is on the first two steps, creating the CallAdapter and the Converter;

Let’s look at the logic of the first step;

The chain of calls looks like this: First the createCallAdapter method of HttpServiceMethod is called, then the callAdapter method of Retrofit is called, and finally the nextCallAdapter method of Retrofit is called.

Let’s look at the logic of nextCallAdapter;

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

The logic here is not too complicated, and we eventually create the CallAdapter through the calladapter.factory, which we’ve already talked about, is the Factory that we use to create the CallAdapter;

Now let’s look at the call in step 2;

The second step of the call chain is to call the createResponseConverter method of HttpServiceMethod and then the responseBodyConverter method of Retrofit. Finally call the Retrofit nextResponseBodyConverter method, let’s take a look at nextResponseBodyConverter the method of logic;

public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
      @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {...int start = converterFactories.indexOf(skipPast) + 1;
   // Create the Converter using the Converter.Factory Factory, and then convert the returned data through the Converter
    for (inti = 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

The logic of the second step is similar to that of the first step. The Converter is finally created using the responseBodyConverter method of the Converter.Factory Factory Factory, which I have already described above.

The convert method is finally called after the OkHttp network request is successful, and the chain of calls is as follows:

1, OkHttpCall enqueue method; 2, OkHttpCall parseResponse method; 3. Convert method of Converter;

Call chain is not very complex, interested friends can follow the source code to have a look, here do not paste the source code;

So at this point, WE’ve finished talking about parseAnnotations for HttpServiceMethod;

After calling the loadServiceMethod method, we call the ServiceMethod Invoke method. This method is an abstract method that is implemented in the subclass;

The final implementation is in the Invoke method of HttpServiceMethod;

Call the CallAdapter adapt method here, will be asked to call into corresponding request type of platform, such as RxJava type, this has also talked above, forget friends, can flip forward;

7,

As can be seen from the above analysis, the most important point of Retrofit is the dynamic proxy. A lot of logic processing has been done during the dynamic proxy, which simplifies our subsequent calls, etc.

Let’s look at the final summary flow chart:

other

Android you have to learn about HTTP

Android network programming TCP, UDP detailed explanation

Android network programming HTTPS detailed explanation

Android network framework OkHttp source code parsing

About me

Brother Dei, if my post is helpful to you, please give me a like ️, and follow me on Github and my blog.