The introduction

Retrofit is a framework for Android web requests developed by Square. It follows a Restful design style. We look at Invoke and find that it is based on OkHttp and has the following advantages over other web request frameworks:

  • Best performance, fastest speed (dynamic proxy advantage)
  • Simple and easy to use, code simplification
  • Thoroughly decoupled, responsibilities subdivided
  • Easy to work with other frameworks (Rxjava)

Of course, there are drawbacks: because of the high encapsulation, the scalability is less than OkHttp, but overall it meets the needs of the project. So today I’m going to take a look at how Retrofit actually works and how does it work

Summary of a.

role

OKHttp is simpler, more convenient, and hides some of OKHttp’s features.

type-safe

Retrofit is based on OKHTTP, so all of Retrofit’s work is around request body and response body. Retrofit provides a variety of converters and you can customize converters to build your request body and serialize the response body to the type you want from the converters. This ensures that both the request body and the response body are secure

Use two.

Add 1.retrofitRely on
implementation 'com. Squareup. Retrofit2: retrofit: 2.5.0'
implementation 'com. Squareup. Retrofit2: converter - gson: 2.5.0'
Copy the code
2. Addnetworkpermissions
<uses-permission android:name="android.permission.INTERNET"/>
Copy the code
3. Create an entity class

Access the github warehouse and obtain the following packets through get request:

class MicroKibacoRepo {


   private int id;
   private String node_id;
   private String name;
   private String full_name;
  // -- to avoid wasting space, omit useless code --
}
Copy the code
4. Create an interface as a collection of Web Service requests, and use annotations to write request methods that need to be configured
public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<MicroKibacoRepo>> listRepos(@Path("user") String user);
}
Copy the code
5. Create an instance of an interface using Retrofit in formal code
  Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();

GitHubService service = retrofit.create(GitHubService.class);
Copy the code
6. Invoke the corresponding method of the created Service instance to create a corresponding Call object that can be used to initiate network requests
 Call<List<MicroKibacoRepo>> repos = service.listRepos("MicroKibaco");

Copy the code
7. Use call.execute () or call.enqueue () to initiate the request
    repos.enqueue(new Callback<List<MicroKibacoRepo>>() {
            @Override
            public void onResponse(Call<List<MicroKibacoRepo>> call, Response<List<MicroKibacoRepo>> response) {}@Override
            public void onFailure(Call<List<MicroKibacoRepo>> call, Throwable t) {}});Copy the code

Common apis for Retrofit

methods role
serviceMethodCache Custom collection of interface mapping objects
baseUrl Request the address
callFactory The default is OKHttpCall
converterFactories A collection of data parser factories
callbackExecutor The callback is executed, and the Android platform defaults to MainThreadExecutor
Platform Retrofit is a way to manage multiple platforms, with support for Android and Java8. Through findPlatform access to the corresponding platform, at the same time also initialize the defaultCallAdapterFactory factory
ServiceMethod The network Request object of interface mapping converts the annotation of custom interface into this object through dynamic proxy, and generates the Request object required by OkHttp by annotation and parameter. Retrofit’s Create transforms each custom interface into a ServiceMethod object via dynamic proxy interception, which is cached via serviceMethodCache
ExecutorCallAdapterFactory The callback is executed, and the Android platform defaults to MainThreadExecutor
CallAdapter.Factory Static factory for CallAdapter, containing abstract methods of GET, used to produce CallAdapter objects
OkHttpCall The Call implementation of OkHttpCall, createRawCall, gets the real OkHttp3. Call object, which is used to make the actual network request
Call Network request interface defined by Retrofit, including execute, enQueue, and other methods
Converter.Factory A data parser factory that produces Converter instances
ExecutorCallAdapterFactory As the default CallAdapter factory of the Android platform, the GET method implements CallAdapter using an anonymous internal class, returns ExecutorCallbackCall, and implements CallExecutorCallbackCall
ExecutorCallbackCall With a static proxy design, the delegate is actually an OkHttpCall, and the callbackExecutor is used to implement the callback in the main thread
RxJavaCallAdapterFactory In the Rxjava platform’s CallAdapter factory, the get method returns the RxJavaCallAdapter object
RxJavaCallAdapter An Rxjava platform allocator that returns an Observable object
GsonConverterFactory Data parsing factory instance, returned to the parser GsonResponseBodyConverter data
GsonResponseBodyConverter Gson’s data parser converts json objects returned by the server into the corresponding Java model

Four. Source code analysis

How to start reading the source code?

Looking at the source code with some questions,Retrofit has a few key processes

  1. How does Retrofit translate defined interfaces into network requests?
  2. How is Retrofit’s Converter mechanism implemented?
  3. How is Retrofit’s CallAdapter mechanism implemented?
1. Find the entrance

Read line by line, is certainly not feasible, too tired, and the brain capacity is not enough to remember, the appropriate way to read the source code is to start with the program entrance. The current function starts reading.

The entry to the current request takes place in Enqueue, so let’s start reading the source code from this Api.

Enqueue, which means to enter a queue, goes in and looks at it

  void enqueue(Callback<T> callback);
Copy the code

If a similar abstract method appears; I have to go back because I can’t read anymore

2. Find the interface implementation

So we go up there, and the Api is EnQueue, and we look for the implementation of the interface, and we cry, and the implementation of the interface is GithubService

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<MicroKibacoRepo>> listRepos(@Path("user") String user);
}
Copy the code

Githubservice.java was created by githubservice.java. I’m not going to dig through the entire source code, but I’m going to go through the key parts. There are apis that I need to talk about:

  • eagerlyValidateMethods
  • newProxyInstance
  • Platform
  • invoke
  • invokeDefaultMethod
  public <T> T create(final Class<T> service) {
   
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) 
    
    // -------- dynamic proxy --------
    Proxy.newProxyInstance(
    // Provide an instance of a dynamic class
    service.getClassLoader(), 
    // Get an instance of GithubService, get the interface that requires the dynamic proxy class, passed in retrofit.create
    newClass<? >[] { service },// 
        new InvocationHandler() {
         JDK 7.0/8.0 or Android/IOS
          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 {
            // If this method is not declared directly, then it is called directly
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
             // Since Java 8, interface execution has default methods implemented
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            returnloadServiceMethod(method).invoke(args ! =null? args : emptyArgs); }}); }static <T> void validateServiceInterface(Class<T> service) {
     // 1. Verify that GitHubService is an interface
    if(! service.isInterface()) {throw new IllegalArgumentException("API declarations must be interfaces.");
    }
     // 2. Verify that GitHubService inherits other interfaces and accepts only native interfaces
    if (service.getInterfaces().length > 0) {
      throw new IllegalArgumentException("API interfaces must not extend other interfaces."); }}// 3. When it is initialized, annotations are read during initialization, which is time-consuming. Distributed network requests are loaded
    private void eagerlyValidateMethods(Class
        service) {
    Platform platform = Platform.get();
    for (Method method : service.getDeclaredMethods()) {
      if(! platform.isDefaultMethod(method)) { loadServiceMethod(method); }}}Copy the code

Java Platform. Java Platform. Java Platform. Java 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");
       / / Java8 platform
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }
Copy the code

LoadServiceMethod also has no invoke, so let’s see if we can find invoke using parseAnnotations.

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

Java, but it’s also an abstract method, so let’s go back to parseAnnotations and see if there’s an invoke in there Annotations parseAnnotations are a subclass of ServiceMethod, and HttpServiceMethod must have an invoke implementation

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    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.");
    }
// Use parseAnnotations to find the invoke implementation
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  abstract T invoke(Object[] args);
}
Copy the code

Invoke (CTR + f); invoke (OKHttp);

final class HttpServiceMethod<ResponseT.ReturnT> extends ServiceMethod<ReturnT> {
 // ---- to avoid wasting space, omit some useless code ----
 @Override ReturnT invoke(Object[] args) {
    return callAdapter.adapt(
        newOkHttpCall<>(requestFactory, args, callFactory, responseConverter)); }}Copy the code

OkHttpCall implements the Call interface. Let’s focus on the methods:

  • createRawCall

 // OKhttp3's Call helps Retrofit implement calls for network requests
  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
  • call.enqueue
     call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
      
      // Response results are given to Retrofit for use
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          throwIfFatal(e);
          callFailure(e);
          return;
        }

        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch(Throwable t) { t.printStackTrace(); }}@Override public void onFailure(okhttp3.Call call, IOException e) {
        callFailure(e);
      }

      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch(Throwable t) { t.printStackTrace(); }}}); }Copy the code

What is dynamic proxy?

Dynamic proxy creates an object that passes in an interface and helps you implement each method of each interface, and each method of each interface points to the Invoke method of each method of each interface.

  • callAdapter

The constructor in HttpServiceMethod. Java, we find the CallAdapter, and we go to the createCallAdapter and return it

  private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter( Retrofit retrofit, Method method) {
    Type returnType = method.getGenericReturnType();
    Annotation[] annotations = method.getAnnotations();
    try {
      / / CallAdapter created
      return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
    } catch (RuntimeException e) { // Wide exception range because factories are user code.
      throw methodError(method, e, "Unable to create call adapter for %s", returnType); }}Copy the code

Go to Retrofit.java and check it out:

  publicCallAdapter<? ,? > callAdapter(Type returnType, Annotation[] annotations) {return nextCallAdapter(null, returnType, annotations);
  }
  
    publicCallAdapter<? ,? > nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
  
 // Get the callAdapter using callAdapterFactories
 
    for (inti = start, count = callAdapterFactories.size(); i < count; i++) { CallAdapter<? ,? > adapter = callAdapterFactories.get(i).get(returnType, annotations,this);
      if(adapter ! =null) {
        returnadapter; }}// ---- to avoid wasting space, omit some useless code ----
  }
Copy the code

The callAdaperFactory is built from Retrofit’s build method.

  public Retrofit build(a) {
  
      converterFactories.addAll(this.converterFactories);
      converterFactories.addAll(platform.defaultConverterFactories());

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

We see callAdaperFactory addAll one-time add to come over, so have we see defaultCallAdapterFactories method.


    @Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
        @Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      ExecutorCallAdapterFactory executorFactory = new ExecutorCallAdapterFactory(callbackExecutor);
      return Build.VERSION.SDK_INT >= 24
        ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
        : singletonList(executorFactory);
    }
Copy the code

Then we could look at the ExecutorCallAdapterFactory

Sharing interview questions

Retrofit request parameter description

Design patterns in Retrofit
1. Builder mode

Both Retrofit object creation and ServiceMethod object creation use Build mode to separate the creation and presentation of complex objects. Callers do not need to know the complex creation process, and use Build related methods to configure the creation of objects.

2. Appearance mode

Retrofit provides uniform scheduling externally, shielding internal implementations, making it easy and convenient to use the network library. Facade pattern: Provides a unified interface to access multiple different interfaces of multiple subsystems. It provides a unified high-level interface for a set of interfaces in a subsystem. Using subsystems is easier to use

3. Dynamic proxy mode

With dynamic proxies, dynamic proxy listening is done when Retrofit’s Create () method is called. InvocationHandler is called back when a specific interface method is executed. The ServiceMethod object is generated by reflecting the annotations and parameters of the method.

4. Static proxy mode

The default Android adapter ExecutorCallbackCall uses the static agent mode. The implementation delegate is OkHttpCall.

5. Factory mode

Both Converter and CallAdapter are created in factory mode.

6. Adapter mode

The CallAdapter ADAPT uses the adapter mode to dynamically expand the return object of an interface, increasing flexibility

The types of CallAdapter

The Converter type

Retrofit supports multiple data parsing methods that require adding dependencies to Gradle.

Retrofit process summary