preface

  • inAndroidNetwork requests are very common in development
  • And in theAndroidIn the network request library,RetrofitIs one of the most popular online request libraries

  • Today, I’m going to walk you through it, hand in handRetrofit v2.0Source code, I hope you will like
  1. Please try to see the picture on the PC rather than the mobile terminal, otherwise it may be difficult to see the picture.
  2. Before reading this article, I recommend reading the article: This is a very detailed tutorial on using Retrofit 2.0 with examples.

directory


1. Introduction

Special attention:

  • To be exact, Retrofit is a RESTful encapsulation of the HTTP web request framework.
  • Reason: Network requests work essentiallyOkHttpWhile Retrofit is only responsible for the encapsulation of the network request interface

  • The App application requests the network through Retrofit, essentially using the Retrofit interface layer to encapsulate the request parameters, headers, URLS, and so on, and then OkHttp completes the subsequent requests
  • After the server returns the data, OkHttp passes the original results to Retrofit, which parses the results based on the user’s needs

2. Compare with other network request open source libraries

In addition to Retrofit, the main web request frameworks in Android today are:

  • Android-Async-Http
  • Volley
  • OkHttp

Here is a brief introduction:

A graph to give you an idea of all the web request libraries and the differences between them!


Attached: Github address of each major network request library

  • Android-Async-Http
  • Volley
  • OkHttp
  • Retrofit

3. The specific use of Retrofit

This is a very detailed tutorial on using Retrofit 2.0 (with examples)


4. Source analysis

4.1 The essential process of Retrofit

The general network communication process is as follows:

  • The essence of Retrofit is the same
  • It’s just that Retrofit makes the above process much simpler & smoother by using a number of design patterns to decouple the functional modules

The diagram below:

The specific process is explained as follows:

  1. Configure network request parameters by parsing network request interface annotations
  2. Generate network request objects through dynamic proxies
  3. The network request object is adapted to the platform through the network request adapter

Platforms include: Android, Rxjava, Guava and Java8

  1. Send a network request through a network request executor
  2. The data returned by the server is parsed through the data converter
  3. Switch threads (subthread ->> main thread) via callback actuator
  4. The user processes the returned results on the main thread

Here are some of the roles mentioned above

Special note: because the following source code analysis is based on the use of the step by step with you debug into, so you must first read the articleThis is a very detailed tutorial on using Retrofit 2.0 (with examples).

4.2 Source code Analysis

Let’s recall the steps of using Retrofit:

  1. Create a Retrofit instance
  2. Create a network request interface instance and configure network request parameters
  3. Send a network request

Encapsulates data conversion, thread switching operations

  1. Process the data returned by the server

4.2.1 Creating a Retrofit Instance

A. Procedure

 Retrofit retrofit = new Retrofit.Builder()
                                 .baseUrl("http://fanyi.youdao.com/")
                                 .addConverterFactory(GsonConverterFactory.create())
                                 .build();
Copy the code

B. Source code analysis

Retrofit instances are created through the Builder class using the Builder pattern

Builder pattern: Separates the construction and presentation of a complex object, allowing users to create complex objects without knowing the details of how the object was created. See the article Builder Pattern – the most understandable analysis of design patterns

Next, I’ll walk you through creating a Retrofit instance in five steps

Step 1

Public final class Retrofit {private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>(); // Network request configuration object (network request interface method comment parsing object) // Function: storage network request related configuration, such as network request method, data converter, network request adapter, network request factory, base address, etc. Private final okhttp3.call.factory callFactory; // Retrofit uses okHTTP private final List< callAdapter.factory > adapterFactories by default; // Retrofit uses okHTTP private final List< callAdapter.factory > adapterFactories by default. // Set of network request adapter factories Production network request adapter (CallAdapter) // Private final List< convert.factory > converterFactories; Private final Executor callbackExecutor; private final Executor callbackExecutor; // Call back method executor private final Boolean validate15; // Flag bit // function: Retrofit(okhttp3.call.factory callFactory, HttpUrl baseUrl, HttpUrl baseUrl) List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories, Executor callbackExecutor, boolean validateEagerly) { this.callFactory = callFactory; this.baseUrl = baseUrl; this.converterFactories = unmodifiableList(converterFactories); this.adapterFactories = unmodifiableList(adapterFactories); UnmodifiableList (list) <E>(list) CallbackExecutor = callbackExecutor; this. CallbackExecutor = callbackExecutor; this.validateEagerly = validateEagerly; . // Post only the key codeCopy the code

The criteria for successfully creating a Retrofit object is to configure the member variables in the Retrofit class:

  • serviceMethod: An object that contains information about all network requests
  • baseUrl: Indicates the URL of the network request
  • callFactory: Network request factory
  • adapterFactories: a collection of network request adapter factories
  • converterFactories: collection of data converter factories
  • callbackExecutor: callback method actuator

The so-called xxxFactory and “XXX factory” are actually the embodiment of the factory pattern in the design pattern: separate the “operation of class instantiation” from the “operation of using object”, so that the user can instantiate the required “product” class without knowing the specific parameters.

For details, see SimpleFactoryPattern – the most understandable design pattern to parse Factory Method – the most understandable design pattern to parse Abstract Factory – The most understandable design pattern analysis

CallAdapterFactory: This Factory produces a CallAdapter, so what is a CallAdapter?

CallAdapterDetailed introduction

  • Definition: An adapter for a network request executor (Call)
  1. The default is Call in RetrofitOkHttpCall
  2. There are four callAdapterFactories available in Retrofit: ExecutorCallAdapterFactory (default), GuavaCallAdapterFactory, Java8CallAdapterFactory, RxJavaCallAdapterFactory
  • Function: Converts the default network request executor (OkHttpCall) into a network request executor form suitable for invocation by different platforms
  1. In the beginningRetrofitOnly intended to useOkHttpCallthroughExecutorCallbackCallSwitch threads; But later found useRxjavaMore convenient (no Handler needed to switch threads). Want to achieveRxjavaYou have to useRxJavaCallAdapterFactoryCallAdapterwillOkHttpCallConverted toRxjava(Scheduler):
// Encapsulate Response as rxJava Observeble, Then for flow operation Retrofit. Builder. AddCallAdapterFactory (newRxJavaCallAdapterFactory (). The create ()); // I won't go into more detail about using RxJavaCopy the code
  1. Retrofit also supports java8 and Guava platforms.
  • Benefits: More platform compatibility with minimum cost, which means more usage scenarios

Therefore, steps 2, 3, 4, and 4 that need to be analyzed next are intended to configure all of the above member variables

Step 2

Let’s look at the Builder class first

Please follow the steps shown below

<-- Builder class --> public static final class Builder {private Platform Platform; private okhttp3.Call.Factory callFactory; private HttpUrl baseUrl; private List<Converter.Factory> converterFactories = new ArrayList<>(); private List<CallAdapter.Factory> adapterFactories = new ArrayList<>(); private Executor callbackExecutor; private boolean validateEagerly; // As you can see from the above, The member variables of the Builder class correspond to the member variables of the Retrofit class, so the member variables of the Retrofit class are basically configured through the Builder classBuilder() { this(Platform.get()); Public Builder(Platform) ->> Step 5 (step 2, step 3, step 4) // And pass the Platform object // by calling platform.get () Get () ->> Step 2 // Remember to look at step 5's Builder constructor}... <-- Step 2 --> class Platform {private static final Platform Platform = findPlatform(); // Assign findPlatform() to the static variable static Platformget() {
    returnPLATFORM; FindPlatform () ->> Step 3} <-- Step 3 --> Private static PLATFORMfindPlatform() {
    try {

      Class.forName("android.os.Build"); // class.forname (xxx.xx.xx) requires the JVM to find and load the specified Class.if(Build.VERSION.SDK_INT ! = 0) {returnnew Android(); // Where: >> Step 4}} Catch (ClassNotFoundException) {} try {// Support Java platform class.forname (ClassNotFoundException)"java.util.Optional");
      returnnew Java8(); } catch (ClassNotFoundException) {} try {// support class.forname ("org.robovm.apple.foundation.NSObject");
      returnnew IOS(); } Catch (ClassNotFoundException) {} Public Builder(Platform Platform) --> Step 5 // Note Builder specifies the running platform as Androidreturnnew Platform(); }... } <-- Step 4 --> static class Android extends Platform {@override CallAdapter.factory defaultCallAdapterFactory(Executor callbackExecutor) {returnnew ExecutorCallAdapterFactory(callbackExecutor); // Create a default network request adapter factory. // The default factory produces adapters that make calls asynchronously callback on the specified Executor. // There are four callAdapterFactories available in Retrofit: ExecutorCallAdapterFactory (default), GuavaCallAdapterFactory, Java8CallAdapterFactory, adopt the strategy pattern RxJavaCallAdapterFactory / /} @Override public ExecutordefaultCallbackExecutor() {// return a default callback method executor. This executor switches threads (child ->> main thread) and executes the callback method in the main thread (UI thread)returnnew MainThreadExecutor(); } static class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); @override public void execute(Runnable r) {handler.post(r); // This is the Handler that is bound to the Android main thread. // This is the Handler that is bound to the Android main thread. }} / switch/thread process: / / 1. The callback ExecutorCallAdapterFactory generates a ExecutorCallbackCall object / / 2. By calling ExecutorCallbackCall. The enqueue MainThreadExecutor (the CallBack) to call the execute () through the handler to switch to the main thread} / / continue to look at step 5 below Builder and construction method Public Builder(Platform Platform) {// receive the Platform object (Android Platform) this.platform = platform; // Configure the converterFactories by passing the BuiltInConverters() object. // The converterFactories are an array that holds the converter.factory // ConverterFactories. Add (new BuiltInConverters()); // BuiltInConverters is a built-in data Converter Factory (inheriting the Converter.factory class) // New BuiltInConverters() is to initialize the data Converter}Copy the code

Builder class analysis is finished, conclusion: Builder set default

  • Platform type object: Android
  • Network request adapter factory: CallAdapterFactory

A CallAdapter is used to reencapsulate the original Call, such as Call to Observable

  • Data converterFactory: converterFactory
  • CallbackExecutor: callbackExecutor

Note that the default values are set, but they are not actually configured into the member variables of the specific Retrofit class

Step 3

Let’s watch it step by step

<-- Step 1 --> public Builder baseUrl(String baseUrl) {// Convert String url parameters to fit OKhttp HttpUrl HttpUrl = HttpUrl.parse(baseUrl); // Return baseUrl() with httpUrl type parameter baseUrl() ->> Step 2returnbaseUrl(httpUrl); <-- Step 2 --> public Builder baseUrl(HttpUrl baseUrl) {List<String> pathSegments = baseUrl.pathSegments(); // Check the last fragment to see if the URL parameter is"/"End // if not, throw an exceptionif (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
        throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
      }		
      this.baseUrl = baseUrl;
      return this;
    }
Copy the code
  • At this point, step 3 analysis is complete
  • Summary: baseUrl () is used to configure the network request URL address for the Retrofit class

Converts the passed String URL to an HttpUrl url suitable for OKhttp

Step 4

We look from inside to outside, namely first see GsonConverterFactory. Creat ()

Public final class GsonConverterFactory extends Converter.Factory {<-- Step 1 --> public final class GsonConverterFactory extends Convertercreate() {// Create a Gson objectreturncreate(new Gson()); <-- Step 2 --> public static GsonConverterFactory create(Gson Gson) {// Create a GsonConverterFactory with an instance of the Gson objectreturnnew GsonConverterFactory(gson); ->> Step 3} private final Gson Gson; <-- Step 3 --> private GsonConverterFactory(Gson Gson) {if (gson == null) throw new NullPointerException("gson == null");
    this.gson = gson;
  }


Copy the code
  • So, GsonConverterFactory. Creat () is to create a GsonConverterFactory containing Gson object instance, and returned to theAddConverterFactory ()
  • Here we go:AddConverterFactory ()
// Put the GsonConverterFactory created above into the converterFactories array // GsonConverterFactory public Builder addConverterFactory(convert.factory) public Builder addConverterFactory(convert.factory factory) { converterFactories.add(checkNotNull(factory,"factory == null"));
      return this;
    }


Copy the code
  • At this point, the analysis is complete
  • Summary: Step 4 is used to create a GsonConverterFactory with an instance of the Gson object and place it in the converterFactories
  1. By default, Retrofit is parsed using Gson
  2. If you use other parsing methods (such as Json, XML, or Protocobuf), you can also use a custom data parser (must inherit from Converter.factory).

Step 5

Finally, the last step.

    public Retrofit build() {<-- configure the network request executor (callFactory) --> okHttp3.call.factory callFactory = this.callFactory; // If not specified, then the default is okhttp // so Retrofit defaults to okHTTP for network requestsif(callFactory == null) { callFactory = new OkHttpClient(); } <-- configure callbackExecutor --> Executor callbackExecutor = this.callBackExecutor; // If not specified, the default callbackExecutor used for Platform detection is used by defaultif(callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); } <-- configure the network request adapter Factory (CallAdapterFactory) --> List< calladapter.factory > adapterFactories = new ArrayList<>(this.adapterFactories); // Adds the calladapter.factory request adapter created in Step 2 to the collection (added at the end of the collector) adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); // Request adapter factory collection store order: custom 1 adapter factory, custom 2 adapter factory... The default adapter factory (ExecutorCallAdapterFactory) < -- configuration data converter factory: ConverterFactory --> // The built-in data converter BuiltInConverters() has been added in Step 2 (added to the first part of the collector) // A Gson converter has been inserted in Step 4 GsonConverterFactory (added to the first two of the aggregator) List< converter.factory > converterFactories = new ArrayList<>(this.converterFactories); // The data converter factory collection stores the default data converter factory (BuiltInConverters), custom 1 data converter Factory (GsonConverterFactory), custom 2 data converter factory (....) / / note: / / 1. Request the proper network adapter and data converters from adapterFactories and converterFactories collection first began to traverse the bottom / / the front has the factory location of a set of the use of the higher authority / / Finally, return a Retrofit object, passing in the configured member variables abovereturn new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }
Copy the code
  • At this point, step 5 analysis is complete
  • Summary: In the last step, all the member variables of class Retrofit are configured using the variables set in the previous step.
  • So, you have successfully created an instance of Retrofit

conclusion

Retrofit uses Builder mode to create a Retrofit instance from the Builder class. The creation details are configured as follows:

  • Platform type object (platform-Android)
  • The URL of the network request (baseUrl)
  • Network Request Factory (callFactory)

The default is OkHttpCall

  • A collection of network request adapterFactories

Nature is to configure the adapter factory network request – default is ExecutorCallAdapterFactory

  • A collection of data converterFactories

Essentially, a data converter factory is configured

  • callbackExecutor

The default callback method executor is used to switch threads (subthread – main thread).

Because of the builder pattern, developers can create Retrofit instances without having to care about configuration details. The Builder pattern is GET.

When creating a Retrofit object, you can handle your needs in more flexible ways, such as using different converters, using different CallAdapters, which gives you the possibility of using RxJava to call Retrofit


2. Create an instance of the network request interface

2.1 Procedure

<-- step 1: Define the class to receive network data --> <-- javabeans. Java --> public class javabeans {.. <-- accessapi. Java --> public interface AccessApi {-- accessapi. Java --> public interface AccessApi {-- accessapi. Java --> public interface AccessApi {-- accessapi. Java --> Retrofit divides the URL of the web request into two parts: 1 part baseurl is set when creating the Retrofit object; 2 part baseurl is set when creating the Retrofit object. The other part is set in the Web request interface (here) // If the URL in the interface is a full URL, then the part set in the creation of the Retrofit object can be set without @get ()."openapi.do? Keyfrom = Yanzhikai&key = 2032414398 & type = data&doctype = json&version = 1.1 & q = car") // Receive network request data method Call<JavaBean> getCall(); // Return type Call<*>, * is the parse data type, JavaBean} <-- Step 3: Create interface class in MainActivity --> AccessApi NetService = Retrofit.create (accessapi.class); <-- Step 4: Encapsulate the URL that sent the request, that is, generate the final network request object --> Call<JavaBean> Call = netService.getCall ();Copy the code

2.2 Source code Analysis

  • Conclusion: Retrofit creates an instance of the network request interface using the create () method via the facade pattern & proxy pattern (and configures the network request parameters via the annotations set in the network request interface)
  1. Facade pattern: Defines a unified interface through which external users can access other interfaces in a subsystem. See Facade Patterns – the most straightforward analysis of design patterns
  2. Proxy mode: The target object is indirectly accessed by accessing the proxy object. See Proxy Patterns – the most understandable design Pattern parsing
  • Step 3 and Step 4 are analyzed as follows:
<-- Step 3: Create an interface class instance in MainActivity --> AccessApi NetService = Retrofit.create (netService.class); <-- Step 4: Encapsulate the URL that sent the request, that is, generate the final network request object --> Call<JavaBean> Call = netService.getCall ();Copy the code

Step 3 Explanation:AccessApi NetService = retrofit.create(NetService.class);

 public <T> T create(final Class<T> service) {

       if(validate15) {// Determine whether eagerlyValidateMethods(service) needs to be verified in advance; // 1. Parse the annotation for each method in the interface and get a ServiceMethod object // 2. Use Method as key to store this object in the LinkedHashMap collection // } // Create a dynamic proxy object for the network request interface; // Create a dynamic proxy object for the network request interface; // create a dynamic proxy object for the network request interface. The dynamic proxy is used to create (and return) an instance of the network request interfacereturn(T) proxy.newProxyInstance (service.getClassLoader(), new Class<? >[] {service}, // Create instance new dynamicallyInvocationHandler() {// pass the implementation of the agent class to the InvocationHandler class as the concrete implementation (explained below) private final Platform Platform = platform.get (); // The Invoke () implementation of the InvocationHandler class performs some useful operations, such as counting execution times, doing initialization and cleanup, checking interface calls, and so on, in addition to performing the real logic (such as forwarding to the real implementation objects again). @Override public Object invoke(Object proxy, Method method, Object... Args) throws Throwable {// Invoke () is introduced. // ServiceMethod ServiceMethod = loadServiceMethod(method); OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);returnserviceMethod.callAdapter.adapt(okHttpCall); }}); } // Pay special attention to //return(T) roxy.newProxyInstance(ClassLoader loader, Class<? >[] interfaces, InvocationHandler getProxyClass(loader, interfaces) .getConstructor(InvocationHandler.class).newInstance(invocationHandler); // With dynamically generated proxy classes, the method of calling interfaces is actually done by calling the Invoke () InvocationHandler object. EagerlyValidateMethods () --> private void eagerlyValidateMethods(Class<? > service) { Platform platform = Platform.get();for (Method method : service.getDeclaredMethods()) {  
      if(! platform.isDefaultMethod(method)) { loadServiceMethod(method); } // Add the passed ServiceMethod object to the LinkedHashMap<Method, ServiceMethod> collection // Lruenen.values ().iterator().next() gets the least frequently used elements of the set and provides an implementation of the Lru algorithm}}Copy the code

Create network interface instances using the facade pattern & proxy pattern:

The appearance mode is used for access, with the proxy mode used inside

1. Appearance mode

  • Facade pattern: Defines a unified interface through which external users can access other interfaces in a subsystem. See Facade Patterns – the most straightforward analysis of design patterns

  • The look of the Retrofit object (store) = retrofit.create()

  • This facade allows you to internally invoke methods to create instances of the network request interface and configure network request parameters

The coupling degree of the system is greatly reduced

2. Proxy mode

  • Proxy mode: The target object is indirectly accessed by accessing the proxy object

Divided into static agent & dynamic agent:

  1. Static proxy: The proxy that existed before the program was run
  2. Dynamic Proxy: The Proxy class does not exist before the program runs and is dynamically generated by the program at run time. For details, see the article Proxy Pattern – the most understandable design Pattern resolution
  • return (T) roxy.newProxyInstance(ClassLoader loader, Class<? >[] interfaces, InvocationHandler invocationHandler)Through the dynamic proxy pattern in the proxy pattern, the proxy class of the network request interface is dynamically generated and the instance of the proxy class is createdInvocationHandler classAs a concrete implementation, and ultimately return a dynamic proxy object.

The instance generation process includes a caching mechanism for generating implementation classes (singleton pattern), which is discussed in detail below

Benefits of using dynamic proxies:

  • whenNetServiceThe objectGetCall ()The invocation is collectively forwarded to InvocationHandler#invoke () for processing
  • Gets all the annotations on the network request interface instance
  • Easier to encapsulate ServiceMethod

Let’s look at the source code analysis

The implementation of the InvocationHandler class # Invoke () will be examined in detail below

 new InvocationHandler() { private final Platform platform = Platform.get(); @Override public Object invoke(Object proxy, Method method, Object... Args) throws Throwable {// Read the method in the network request interface, and configure the serviceMethod object according to the previously configured properties serviceMethod serviceMethod = loadServiceMethod(method); OkHttpCall okHttpCall = new okHttpCall <>(serviceMethod, args); // Concern 3 // Function: Call OkHttp and return reJava's Observe object or Call based on okHttpCallreturn serviceMethod.callAdapter.adapt(okHttpCall);
          }
Copy the code

The code for the three concerns is detailed below.

Concern 1:ServiceMethod serviceMethod = loadServiceMethod(method);

<-- loadServiceMethod(method) --> // A ServiceMethod object corresponds to a method in the network request interface ServiceMethod: ServiceMethod loadServiceMethod(Method Method) {ServiceMethod result; // Synchronized (serviceMethodCache) {result = servicemethodCache.get (method); // Before creating a ServiceMethod object, check whether the ServiceMethod cache cache the network request instances that have been created before. // If the ServiceMethod object is not cached, The serviceMethod object is created through the builder patternif(result == null) {result = new ServiceMethod.Builder(this, method).build(); serviceMethodCache.put(method, result); }}returnresult; }}}}}}}}}}}}}}}}}}} Since the class object is passed in each instance of the interface, and the class object is an in-process singleton, the same Method instance that was obtained is also a singleton, so the cache is valid here.Copy the code

Next, I’ll break down the process of creating a serviceMethod instance in three steps:

Step 1:ServiceMethod classThe constructor

<-- ServiceMethod class --> public final class ServiceMethod {final okhttp3.call.factory callFactory; // Final CallAdapter<? > callAdapter; // The network request adapter factory is created in new Servicemethod.builder (this, Private final Converter<ResponseBody, T> responseConverter; private final Converter<ResponseBody, T> responseConverter; // Response content converter: converts the data returned by the server (JSON or other format, enclosed by the ResponseBody) into an object of type T; private final HttpUrl baseUrl; Private final String relativeUrl; // The relative address of the network request private final String httpMethod; Private final Headers Headers; // HTTP request header key/value pair private Final MediaType contentType; // HTTP request body type private final ParameterHandler<? >[] parameterHandlers; // The method parameter handler is responsible for parsing the parameters of each method defined by the API and setting the parameters when constructing the HTTP request. The ServiceMethod object contains all the basic information needed to access the network. <-- ServiceMethod class constructor --> Introduced into various network request parameter ServiceMethod (Builder < T > Builder) {this. CallFactory = Builder. Retrofit. CallFactory (); this.callAdapter = builder.callAdapter; this.responseConverter = builder.responseConverter; this.baseUrl = builder.retrofit.baseUrl(); this.relativeUrl = builder.relativeUrl; this.httpMethod = builder.httpMethod; this.headers = builder.headers; this.contentType = builder.contentType; . this.hasBody = builder.hasBody; y this.isFormEncoded = builder.isFormEncoded; this.isMultipart = builder.isMultipart; this.parameterHandlers = builder.parameterHandlers; }Copy the code

Step 2:ServiceMethod Builder ()

public Builder(Retrofit retrofit, Method method) { this.retrofit = retrofit; this.method = method; This.methodanannotations = method.getanannotations (); this.methodanannotations = method.getanannotations (); / / access network request parameter types in the interface methods enclosing parameterTypes = method. The getGenericParameterTypes (); / / the annotation in the request for network interface method content enclosing parameterAnnotationsArray = method. The getParameterAnnotations (); }Copy the code

Step 3:Build () of ServiceMethod

// Function: control the generation process of ServiceMethod objects public ServiceMethodbuild() { callAdapter = createCallAdapter(); ResponseType = callAdapter.responseType(); // Get the corresponding network request adapter from the Retrofit object based on the return value and annotation type of the network request interface method. ResponseConverter = createResponseConverter(); // Get the corresponding data converter from the Retrofit object based on the return value and annotation type of the network request interface method. The arguments we pass are String // The Retrofit class provides a converter to convert all the passed arguments to String // The rest of the arguments are converted using the stringConverter of the Converter.Factory // The @body and @part arguments are converted using the requestBodyConverter provided by the Converter.Factory // These three converters are supplied by "asking" a list of factories. The factory list, on the other hand, can be added when the Retrofit object is constructed.for(Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation); } // Http_request () {// http_request () {// http_request (); DELETE, GET, POST, HEAD, PATCH, PUT, OPTIONS, HTTP, Retrofit2.http.Headers, Multipart, FormUrlEncoded // Processing is mainly call methods parseHttpMethodAndPath(String httpMethod, String value, Boolean hasBody) Assign values to the httpMethod, hasBody, relativeUrl, and relativeUrlParamNames fields in ServiceMethod. Int parameterCount = parameterAnnotationsArray.length; // Get the number of parameters for the current method parameterHandlers = new ParameterHandler<? >[parameterCount];for(int p = 0; p < parameterCount; p++) { Type parameterType = parameterTypes[p]; Annotation[] parameterAnnotations = parameterAnnotationsArray[p]; // Create a ParameterHandler for each parameter in the method. // The process of creating this object is to parse the annotations in the method parameters. // The annotations include: [p] = parseParameter(p, parameterType, p) parameterAnnotations); }returnnew ServiceMethod<>(this); <-- summary --> // 1. Get the corresponding network request adapter and Response content converter from the network request adapter factory set and content converter factory set of the Retrofit object according to the return value type and method annotation; // 2. Assign a value to the ServiceMethod field according to the method annotation // 3. Finally, the annotation for each method parameter is parsed to get a ParameterHandler<? This object holds a Request content converter -- either a Request content converter or a String content converter from Retrofit's content converter factory collection, depending on the type of the parameter. } <-- concern 1: createCallAdapter() --> Private CallAdapter<? >createCallAdapter() {// Get the return value Type of the method in the network request interfacereturnType = method.getGenericReturnType(); }} @get annotations [] annotations = method.getanannotations ();}}}}} try {return retrofit.callAdapter(returnType, annotations); Retrofit.calladapter () -- > Concern 2}... retrofit.callAdapter () Retrofit.calladapter () --> public callAdapter <? > callAdapter(TypereturnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations); } public CallAdapter<? > nextCallAdapter(CallAdapter.Factory skipPast, TypereturnType, The Annotation [] annotations) {/ / creating CallAdapter following / / traverse CallAdapter Factory set to find suitable Factory (the Factory set the first step in structural Retrofit Object (described in the first step)) // If no factory ends up providing the required CallAdapter, an exception will be thrownfor(int i = start, count = adapterFactories.size(); i < count; i++) { CallAdapter<? > adapter = adapterFactories.get(i).get(returnType, annotations, this);		
      if(adapter ! = null) {returnadapter; }} <-- focus 3: createResponseConverter () -->createResponseConverter() { Annotation[] annotations = method.getAnnotations(); Try {// responseConverter is still provided by the Retrofit class --> Concern 4return retrofit.responseBodyConverter(responseType, annotations);
      } catch (RuntimeException e) { 
        throw methodError(e, "Unable to create converter for %s", responseType); }} <-- focus 4: responseBodyConverter() --> public <T> Converter<ResponseBody, T> responseBodyConverter(Type)type, Annotation[] annotations) {
    return nextResponseBodyConverter(null, type, annotations);
  }

 public <T> Converter<ResponseBody, T> nextResponseBodyConverter(Converter.Factory skipPast,

    int start = converterFactories.indexOf(skipPast) + 1;
    for(int i = start, count = converterFactories.size(); i < count; I++) {// get Converter<ResponseBody,? > converter = converterFactories.get(i).responseBodyConverter(type, annotations, this); // Walk through the Converter.Factory collection and find the appropriate Factory (the Factory collection is added when the Retrofit object is constructed (as explained in the first step)) // Since the Retroifit is constructed using Gson parsing, So take the GsonResponseBodyConverter / / Retrofit - Converters also provides a JSON, XML, ProtoBuf and other types of data conversion function. ResponseBodyConverter () --> responseBodyConverter () --> @override public Converter<ResponseBody,? > responseBodyConverter(Typetype, Annotation[] annotations, Retrofit retrofit) { TypeAdapter<? > adapter = gson.getAdapter(TypeToken.get(type)); // Based on the target type, use Gson#getAdapter getAdapter
  returnnew GsonResponseBodyConverter<>(gson, adapter); } // Use Gson's API to convert data. final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> { private final Gson gson; private final TypeAdapter<T> adapter; GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) { this.gson = gson; this.adapter = adapter; } @Override public T convert(ResponseBody value) throws IOException { JsonReader jsonReader = gson.newJsonReader(value.charStream()); try {returnadapter.read(jsonReader); } finally { value.close(); }}}Copy the code
  • When RxjavaCallAdapterFactory is selected, Rxjava selects the corresponding Adapter through the policy mode

For an explanation of Strategy patterns, see Strategy Patterns – the most understandable analysis of design patterns

  • The process is to select which CallAdapterFactory to use based on the return value type of the network interface method, and then create a specific CallAdapter instance

Factory mode is adopted to make each functional module highly decoupled

  • There are two factories mentioned above: calladapter.factory and Converter.factory, each responsible for providing different functional modules
  • How and what kind of functional modules is the factory responsible for providing
  • Retrofit is only responsible for providing decision information about which factory to choose (such as parameters of network interface methods, return value types, annotations, etc.)

This is called high cohesion low coupling, factory mode GET.

See my article on the factory model:

SimpleFactoryPattern – the most understandable design pattern parsing

Factory Method – the most understandable design pattern parsing

Abstract Factory – The most understandable analysis of design patterns

Finally, the network request parameters (that is, the ServiceMethod object) are configured. The second line, the creation of the okHttpCall object, is covered next

The second line:OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);

Create the okHttpCall object based on the ServiceMethod object configured in the first step and the request parameters entered

<--OkHttpCall class --> public class OkHttpCall {private final ServiceMethod<T> ServiceMethod; Private final Object[] args; Private okHttp3.Call rawCall; Private Throwable creationFailure; private Throwable creationFailure; Private Boolean executed; private volatile boolean canceled; Public OkHttpCall(ServiceMethod<T> ServiceMethod, Object[] args) {// Pass the configured ServiceMethod Object and the input request parameter this.servicemethod = ServiceMethod; this.args = args; }Copy the code

The third line:return serviceMethod.callAdapter.adapt(okHttpCall);

Pass the OkHttpCall object created in step 2 to adapt () of the corresponding network request adapter factory in the serviceMethod object created in Step 1

Return object type: The default for Android is Call<>; If RxJavaCallAdapterFactory is set, it returns Observable<>

Adapt () adapt() adapt() adapt() adapt() adapt() adapt();returnnew ExecutorCallbackCall<>(callbackExecutor, call); } ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) { this.delegate = delegate; // Delegate the OkhttpCall object to the static proxy. // Both the static and dynamic proxies are in the proxy mode. CallbackExecutor = callbackExecutor; this. CallbackExecutor = callbackExecutor; // Pass in the callback method executor defined above // for thread switching}Copy the code
  • ExecutorCallbackCall = decorator, whereas OkHttpCall is the one that actually executes the network request
  • Reason to use decorator mode: You want to do something extra when OkHttpCall sends a request. The extra operation here is the thread conversion, which is the child thread switching to the main thread
  1. OkHttpCall enqueue() is a network asynchronous request: when you call okHttpCall.enqueue (), the callback is in the child thread and needs to be converted to the main thread via Handler. ExecutorCallbackCall is for thread callbacks;
  2. This, of course, is how native Retrofit uses toswitch threads. If you’re using Rxjava, you’re not using this ExecutorCallbackCall, you’re using Rxjava calls, so I’m not going to go into that

Step 4 Explanation:Call<JavaBean> call = NetService.getCall();

  • NetServiceObject is actually a dynamic proxy objectThe Proxy newProxyInstance ()(explained in Step 3) is not an object created by the real network request interface
  • whenNetServiceThe objectGetCall ()Will be dynamically proxy objectsThe Proxy newProxyInstance ()Intercept and then call its ownInvocationHandler # invoke ()
  • invoke(Object proxy, Method method, Object... args)Three arguments are passed:Object proxy:(Proxy object),Method method(callgetCall())Object... argsMethod parameters, i.eGetCall (*)The *)
  • Next, use Java reflection to obtainGetCall ()With the args parameterServiceMethod object.

As described in Step 3 above, it will not be explained again here

Finally create and return oneOkHttpCallCall object of type

  1. OkHttpCallA class isOkHttpThe wrapper class
  2. To create theOkHttpCallA Call object of type cannot send network requests yet and needs to be createdRequestObject to send a network request

conclusion

Retrofit uses the appearance pattern to create an instance of the network request interface and configure the network request parameters. The details are:

  • Dynamically create instances of the network request interface ** (proxy pattern – dynamic proxy) **
  • createserviceMethodObject ** (Builder pattern & Singleton pattern (caching mechanism)) **
  • rightserviceMethodObject for network request parameter configuration: Get the corresponding network request URL, network request actuator, network request adapter & data converter from the Retrofit object by parsing the parameters, return values, and annotation types of the network request interface method.(Strategic Mode)
  • rightserviceMethodObject is added to the operation of thread switching, so that after receiving data, the Handler can switch from the child thread to the main thread so that the returned data can be processed ** (decorative mode) **
  • Finally create and return oneOkHttpCallType of network request object

3. Execute the network request

  • RetrofitUse the defaultOkHttp, i.e.,OkHttpCall class(realizedretrofit2.Call<T>Interface)

However, you can customize the Call class you want

  • OkHttpCallTwo network request modes are provided:
    1. Synchronous request:OkHttpCall.execute()
    2. Asynchronous request:OkHttpCall.enqueue()

These two types of network requests are described in detail below.

The enqueue () and execute () of OkHttpCall are not analyzed here. Interested readers can see the source code of OkHttp

3.1 Synchronization RequestOkHttpCall.execute()

3.1.1 Sending request Process

  • Step 1:For each parameter in the method of the network request interfaceParameterHandlerI’m going to parse it, and then I’m going toServiceMethodObject to create aOkHttptheRequestobject
  • Step 2:useOkHttptheRequestSend network requests;
  • Step 3:The returned data is parsed using the GsonConverterFactory set up earlier to get oneResponse<T>object

3.1.2 Usage

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

The above simple line of code, in fact, contains the entire sending network synchronization request in three steps.

3.1.3 Source code analysis

@Override public Response<T> execute() throws IOException { okhttp3.Call call; // synchronized (this) {call = rawCall;if(call == null) { try { call = rawCall = createRawCall(); / / step 1: create a OkHttp Request object Request - > attention 1} the catch (IOException | RuntimeException e) {creationFailure = e; throw e; }}}returnparseResponse(call.execute()); // Step 3: parseResponse () --> focus on 2} <-- Focus on 1: createRawCall() --> private okhttp3.Call createRawCall() throws IOException { Request request = serviceMethod.toRequest(args); / / from ServiceMethod toRequest () returns a Request object okhttp3. Call Call = ServiceMethod. CallFactory. NewCall (Request); // Create an okHttp3.request from the serviceMethod and request objectsif (call == null) {
    throw new NullPointerException("Call.Factory returned null.");
  }
  returncall; } <-- focus on 2: ParseResponse () --> Response<T> parseResponse(okHttp3. Response rawResponse) throws IOException {ResponseBody rawBody = rawResponse.body(); rawResponse = rawResponse.newBuilder() .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())) .build(); Int code = rawResponse.code(); int code = rawResponse.code();if (code < 200 || code >= 300) {
  }

  if (code == 204 || code == 205) {
    returnResponse.success(null, rawResponse); } ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody); try { T body = serviceMethod.toResponse(catchingBody); // After the Http request returns & passes the status code check, pass the response body into the ServiceMethod, ServiceMethod converts the Response body into a Java object by calling the Converter interface (GsonConverterFactory set earlier), which parses the returned data // to generate the Response classreturnResponse.success(body, rawResponse); } catch (RuntimeException e) { ... // Exception handling}}Copy the code

Special attention:

  • ServiceMethodIt holds almost all the data needed for a network request
  • When you send a network request,OkHttpCallNeed fromServiceMethodGets a Request object in the
  • When parsing the data, you also need to passServiceMethoduseConverter(Data converter) converts to Java objects for data parsing

For efficiency, Retrofit also caches parsed request ServiceMethod in Map

serviceMethodCache = new LinkedHashMap<>(); Object, the singleton pattern mentioned in step 2
,>

  • Description of status codes during status code check:

So that’s the whole process of sending network requests in a synchronous manner.

3.2 Asynchronous RequestOkHttpCall.enqueue()

3.2.1 Sending request Process
  • Step 1:For each parameter in the method of the network request interfaceParameterHandlerI’m going to parse it, and then I’m going toServiceMethodObject to create aOkHttptheRequestobject
  • Step 2:useOkHttptheRequestSend network requests;
  • Step 3:The returned data is parsed using the GsonConverterFactory set up earlier to get oneResponse<T>object
  • Step 4: Perform a thread switch to process the returned data results on the main thread

If RxJava is used, the callback goes directly to the main thread

The process for an asynchronous request is similar to that for a synchronous request, except that for an asynchronous request, the callback method is given to the callback executor to execute on the specified thread.

The thread specified here is the main thread (UI thread)

3.2.2 Specific usage
call.enqueue(new Callback<JavaBean>() {
            @Override
            public void onResponse(Call<JavaBean> call, Response<JavaBean> response) {
                System.out.println(response.isSuccessful());
                if (response.isSuccessful()) {
                    response.body().show();
                }
                else{ try { System.out.println(response.errorBody().string()); } catch (IOException e) { e.printStackTrace(); }; }}Copy the code
  • From the above analysis, there are:callIs a static proxy
  • The purpose of using a static proxy is to perform additional operations before and after okhttpCall sends a network request

The additional operation here is a thread switch, which is to switch the child thread to the main thread so that the returned data results are processed on the main thread

3.2.3 Source code analysis
<-- call.enqueue () --> @override public void enqueue(final Callback<T> Callback) {delegation.enqueue (new Callback<T>() @override public void onResponse(Call<T> Call, Override public void onResponse(Call<T> Call, Final Response<T> Response) {// Step 4: Thread switch to display the result callbackExecutor.execute(new) on the main threadRunnable() {// return the result of the Okhttp asynchronous request to the callbackExecutor // callBackexecutor.execute () return the result to the main thread via the Handler asynchronous callback for processing (e.g. display in the Activity, etc.), 2 @override public void? 2 @override public voidrun() {
              if (delegate.isCanceled()) {
                callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
              } else{ callback.onResponse(ExecutorCallbackCall.this, response); }}}); } @Override public void onFailure(Call<T> call, final Throwable t) { callbackExecutor.execute(newRunnable() {
            @Override public void run() { callback.onFailure(ExecutorCallbackCall.this, t); }}); }}); } <-- parse 1: delegation.enqueue () --> @override public void enqueue(final Callback<T> Callback) {okhttp3.call Call; Throwable failure; // Step 1: Create an OkHttp Request object and encapsulate it as an okhttp. call delegate. Create a Request object for OkHttp and encapsulate it as okhttp.call 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; }} step 2: Send the network request // The delegate is the static proxy of OkHttpcall // The static delegate will call okhttp.enqueue (new okHttp3).Callback() { @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) throws IOException { Response<T> response; Try {// Step 3: Parse the returned data 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(); }}}); <-- analysis 2: thread switch after asynchronous request --> // The thread switch is created by the Platform when it detects that the running environment is Android when the Retrofit object is originally created: Static Class Android extends Platform {// Create the default callback actuator factory // If you don't use RxJava and Retrofit together, Use the default calladapter.factory // We'll look at how RxJava is used with Retrofit defaultCallAdapterFactory(Executor callbackExecutor) {return new ExecutorCallAdapterFactory(callbackExecutor);
    }

    @Override 
      public Executor defaultCallbackExecutor() {// Return a default callback method executor // The executor responsible for executing the callback method in the main thread (UI thread)returnnew MainThreadExecutor(); } static class MainThreadExecutor implements Executor {private final Handler Handler = new Handler(Looper.getMainLooper()); @override public void execute(Runnable r) {// Retrofit gets the handler from the main thread // Then the UI thread performs the data display after the network request callback. handler.post(r); }} / switch/thread process: / / 1. The callback ExecutorCallAdapterFactory generates a ExecutorCallbackCall object / / 2. By calling ExecutorCallbackCall. The enqueue MainThreadExecutor (the CallBack) to call the execute () through the handler to switch to the main thread processing returns results (as shown in the Activity, etc.)}Copy the code

So that’s the whole process of sending network requests asynchronously.


5. To summarize

Retrofit is essentially a RESTful HTTP web request framework that encapsulates OkHttp with a number of design patterns for simplicity and ease of use. The specific process is as follows:

  1. RetrofitHttpRequest abstracted intoJavainterface
  2. Network request parameters are described and configured using annotations in the interface
  3. Using dynamic proxy, the network request interface annotations are dynamically resolved intoHTTPrequest
  4. The last executionHTTPrequest

Finally, I post a very detailed Retrofit source code analysis diagram:

6. The final

  • After reading this article, I believe you are very familiar with itRetrofit 2.0Source code analysis
  • aboutRetrofit 2.0For a detailed tutorial, see the articleThis is a very detailed tutorial on using Retrofit 2.0 (with examples).
  • Next, I’ll continue to analyze RxJava in conjunction with Retrofit, and keep an interest in Carson_Ho’s Android development notes

Thumb up, please! Because your encouragement is the biggest motivation for my writing!


Welcome to follow carson_ho’s official wechat account