Retrofit version 2.9.0 main process analysis

Get into the habit of writing together! This is the second day of my participation in the "Nuggets Daily New Plan · April Update Challenge", click to view the details of the event .

Introduction

Retrofit turns your HTTP API into a Java interface.

Retrofit converts HTTP API to Java interface

This article is based on the Retrofitsource code analysis of version 2.9.0!

Dependency added:

com.squareup.retrofit2:retrofit:2.9.0

use

Let's take the Demo of the official website as an example to see the simplest use:

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
} ①

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build(); ②

GitHubService service = retrofit.create(GitHubService.class); ③

Call<List<Repo>> repos = service.listRepos("octocat"); ④

repos.enqueue() ⑤
复制代码

The overall process is divided into four steps:

  1. Create an interface, the internal methods are all methods called by the interface, and use annotations to add request types, parameters, and callback data;
  2. Build an Retrofitobject and set the interface baseUrl;
  3. Use retrofit.create(class)the proxy to create an instance of the interface we created in the first step;
  4. Use the interface instance to perform specific interface calls and return Callobjects;
  5. Use call.enqueu()methods to perform network requests.

The first step does not need specific analysis, just need to know that if we use annotations to add the content of our request, how to parse these annotations will be presented in the third step, let’s look directly at Retrofitthe creation process of the second step.

Create Retrofit

Retrofit.Builder (). BaseUrl (string)

retrofit2.Retrofit.Builder#baseUrl(java.lang.String)

public Builder baseUrl(String baseUrl) {
  Objects.requireNonNull(baseUrl, "baseUrl == null");
  return baseUrl(HttpUrl.get(baseUrl)); ①
}

public Builder baseUrl(HttpUrl baseUrl) {
  Objects.requireNonNull(baseUrl, "baseUrl == null");
  List<String> pathSegments = baseUrl.pathSegments();
  if (!"".equals(pathSegments.get(pathSegments.size() - 1))) { ②
    throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
  }
  this.baseUrl = baseUrl; ③
  return this;
}
复制代码

Retrofit.Builder().baseUrl(string)A total of three operations were done:

  1. urlEncapsulate the incoming object into an HttpUrlobject;
  2. Determine urlwhether it is the /end, if not, throw an exception directly;
  3. will be HttpUrlassigned to Builder.baseUrl.

Builder.build()

retrofit2.Retrofit.Builder#build

public Retrofit build() {
    // baseUrl不能为空
    if (baseUrl == null) {
      throw new IllegalStateException("Base URL required.");
    } ①
    
    // 获取平台,Java/Android
    Platform platform = Platform.get(); ②

    // 创建OkHttpClient对象
    okhttp3.Call.Factory callFactory = this.callFactory;
    if (callFactory == null) {
      callFactory = new OkHttpClient();
    } ③

    // 创建回调线程池对象
    Executor callbackExecutor = this.callbackExecutor;
    if (callbackExecutor == null) {
      callbackExecutor = platform.defaultCallbackExecutor();
    } ④

    // 创建适配器列表,并且添加默认的适配器
    List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
    List<? extends CallAdapter.Factory> defaultCallAdapterFactories =
        platform.createDefaultCallAdapterFactories(callbackExecutor);
    callAdapterFactories.addAll(defaultCallAdapterFactories); ⑤

    // 创建默认的转换器列表
    List<? extends Converter.Factory> defaultConverterFactories =
        platform.createDefaultConverterFactories();
    int defaultConverterFactoriesSize = defaultConverterFactories.size();
    List<Converter.Factory> converterFactories =
        new ArrayList<>(1 + this.converterFactories.size() + defaultConverterFactoriesSize);

    // Add the built-in converter factory first. This prevents overriding its behavior but also
    // ensures correct behavior when using converters that consume all types.
    converterFactories.add(new BuiltInConverters());
    converterFactories.addAll(this.converterFactories);
    converterFactories.addAll(defaultConverterFactories); ⑥

    // 创建Retrofit对象
    return new Retrofit(
        callFactory,
        baseUrl,
        unmodifiableList(converterFactories),
        defaultConverterFactoriesSize,
        unmodifiableList(callAdapterFactories),
        defaultCallAdapterFactories.size(),
        callbackExecutor,
        validateEagerly); ⑦
  }
}
复制代码

Builder.build()The main method is to create or obtain some important objects, including a total of 6 objects, the following analysis of the specific functions:

  1. 第一步判断baseUrl是否为空,为空直接抛异常;
  2. 第二步获取Platform平台对象,因为我们在Android工程中使用,所以获取的是Android21或者Android24其中一个,Platform定义了回调线程池、调用适配器和转换适配器对象等;
  3. 第三步获取回调线程池对象,会从Platform对象中获取默认的线程池;
  4. 第四步获取CallAdapter.Factory列表,并且添加从Platform中获取到的默认CallAdapter.Factory对象。CallAdapter的主要作用就是将我们接口返回的对象转换成想要的类型,典型的就是RxJavaCallAdapter,它就是将默认的Call转换成Observable
  5. 第五步获取Converter.Factory列表,同时也会从Platform中获取默认的Converter.Factory列表。Converter.Factory的主要作用就是将请求结果通过具体的操作转换成想要的类型,典型的就是GsonConverter,它就是将String转换成具体的数据类对象。
  6. 第六步创建Retrofit对象,然后设置上面获取的对象。

Retrofit.cerate()

retrofit2.Retrofit#create

public <T> T create(final Class<T> service) {
  validateServiceInterface(service);
  return (T)
      Proxy.newProxyInstance( ①
          service.getClassLoader(),
          new Class<?>[] {service},
          new InvocationHandler() {
            private final Object[] emptyArgs = new Object[0];

            @Override
            public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                throws Throwable {
              // 判断Service是接口还是类,如果是类直接调用类的方法
              if (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
              }
              args = args != null ? args : emptyArgs;
              Platform platform = Platform.get();
              return platform.isDefaultMethod(method)
                  ? platform.invokeDefaultMethod(method, service, proxy, args)
                  : loadServiceMethod(method).invoke(args); ②
            }
          });
}
复制代码

Retrofit.create(class)方法中可以看出主要的有两步:

  1. 第一步使用动态代理模式,根据传入的class对象代理最终代理出一个实例;
  2. 第二步先判断platform.isDefaultMethod(method),进入Android21类中此方法默认false,所以直接看loadServiceMethod(method)方法即可
    • loadServiceMethod(method)会创建一个ServiceMethod对象;
    • 再调用ServiceMethod.invoke(args)方法,这里传入的接口方法的参数

Retrofit.loadServiceMethod()

retrofit2.Retrofit#loadServiceMethod

ServiceMethod<?> loadServiceMethod(Method method) {
  // 查看是否命中缓存
  ServiceMethod<?> result = serviceMethodCache.get(method);
  if (result != null) return result;

  synchronized (serviceMethodCache) {
    // 加锁并且双重判断是否命中缓存
    result = serviceMethodCache.get(method);
    if (result == null) {
      // 获取新的ServiceMethod对象,并存入Map中
      result = ServiceMethod.parseAnnotations(this, method);
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}
复制代码

loadServiceMethod()方法内部逻辑还是比较简单,先是从Map中获取是否已经存在,不存在再加锁去获取新的ServiceMethod对象,最后放入缓存Map中。具体的获取流程还需要进入ServiceMethod.parseAnnotations(retrofit,method)方法中。

ServiceMethod.parseAnnotations()

retrofit2.ServiceMethod#parseAnnotations

static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
  // 创建RequestFactory对象
  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);
  }
  // 返回类型不能为void类型
  if (returnType == void.class) {
    throw methodError(method, "Service methods cannot return void.");
  } ②
  // 创建HttpServiceMethod对象
  return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); ③
}
复制代码

parseAnnotations()方法中主要做了三件事:

  1. 第一件就是创建了RequestFactory对象,这个接下来分析下这个对象作用是什么;
  2. 第二件通过Method获取方法返回值,并判断可用性;
  3. 第三件转到HttpServiceMethod中创建HttpServiceMethod对象,此类是ServiceMethod的子类。

RequestFactory.parseAnnotations(retrofit, method)

retrofit2.RequestFactory#parseAnnotations

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

Builder(Retrofit retrofit, Method method) {
  this.retrofit = retrofit;
  this.method = method;
  this.methodAnnotations = method.getAnnotations();
  this.parameterTypes = method.getGenericParameterTypes();
  this.parameterAnnotationsArray = method.getParameterAnnotations();
}

RequestFactory build() {
  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation); ①
  }
  // httpMethod不能为空,也就是设置的请求方式GET、POST等
  if (httpMethod == null) {
    throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.).");
  } ②

  // 判断请求提是否合规
  if (!hasBody) {
    if (isMultipart) {
      throw methodError(
          method,
          "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
    }
    if (isFormEncoded) {
      throw methodError(
          method,
          "FormUrlEncoded can only be specified on HTTP methods with "
              + "request body (e.g., @POST).");
    }
  } ③

  int parameterCount = parameterAnnotationsArray.length;
  parameterHandlers = new ParameterHandler<?>[parameterCount];
  for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
    parameterHandlers[p] =
        parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
  } ④

  if (relativeUrl == null && !gotUrl) {
    throw methodError(method, "Missing either @%s URL or @Url parameter.", httpMethod);
  }
  if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
    throw methodError(method, "Non-body HTTP method cannot contain @Body.");
  }
  if (isFormEncoded && !gotField) {
    throw methodError(method, "Form-encoded method must contain at least one @Field.");
  }
  if (isMultipart && !gotPart) {
    throw methodError(method, "Multipart method must contain at least one @Part.");
  }

  return new RequestFactory(this); ⑤
}
复制代码

parseAnnotations()方法直接调用了内部Builder.build()方法,build()方法中,可以细分为五步:

  1. 第一步通过parseMethodAnnotation()解析方法上注解,解析的内容包括:请求方式、请求头、请求地址、是否Multipart和是否FormUrlEncode等信息,下面会详细分析;
  2. 第二步判断请求方式,如果为空直接抛异常;
  3. 第三步判断MultipartFormUrlEncode注解,如果有请求体的时候,不可以有前面两个注解,否则直接抛异常;
  4. 第四步通过parseParameter解析参数上注解,包括BodyFiledHeaderQuery等信息;
  5. 第五步创建RequestFactory对象,将Builder传入进去。

RequestFactory.parseMethodAnnotation()

retrofit2.RequestFactory.Builder#parseMethodAnnotation

private void parseMethodAnnotation(Annotation annotation) {
  if (annotation instanceof DELETE) {
    parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
  } else if (annotation instanceof GET) {
    parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
  } else if (annotation instanceof HEAD) {
    parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
  } else if (annotation instanceof PATCH) {
    parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
  } else if (annotation instanceof POST) {
    parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
  } else if (annotation instanceof PUT) {
    parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
  } else if (annotation instanceof OPTIONS) {
    parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
  } else if (annotation instanceof HTTP) {
    HTTP http = (HTTP) annotation;
    parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
  } else if (annotation instanceof retrofit2.http.Headers) {
    String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
    if (headersToParse.length == 0) {
      throw methodError(method, "@Headers annotation is empty.");
    }
    headers = parseHeaders(headersToParse);
  } else if (annotation instanceof Multipart) {
    if (isFormEncoded) {
      throw methodError(method, "Only one encoding annotation is allowed.");
    }
    isMultipart = true;
  } else if (annotation instanceof FormUrlEncoded) {
    if (isMultipart) {
      throw methodError(method, "Only one encoding annotation is allowed.");
    }
    isFormEncoded = true;
  }
}
复制代码

parseMethodAnnotation()主要就是解析出方法上的注解,比如是GET还是POST请求,有没有额外添加HEADER,或者是不是MultipartFormUrlEncoded

RequestFactory.parseParameter()

retrofit2.RequestFactory.Builder#parseParameter

private @Nullable ParameterHandler<?> parseParameter(
    int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
  ParameterHandler<?> result = null;
  if (annotations != null) {
    for (Annotation annotation : annotations) {
      ParameterHandler<?> annotationAction =
          parseParameterAnnotation(p, parameterType, annotations, annotation);

      if (annotationAction == null) {
        continue;
      }

      if (result != null) {
        throw parameterError(
            method, p, "Multiple Retrofit annotations found, only one allowed.");
      }

      result = annotationAction;
    }
  }
.....
  return result;
}
复制代码

parseParameter()具体的解析流程在parseParameterAnnotation()方法中,由于此方法源码过于长,此处就不再贴源码了,里面其实就是根据不同的注解类型来解析成对应的ParameterHandler对象。

具体的类型如下:

  • @Url注解,解析出来对应的是ParameterHandler.RelativeUrl()对象;
  • @Path注解,解析出来对应的是ParameterHandler.Path()对象;
  • @Query注解,解析出来对应的是ParameterHandler.Query()对象;
  • @QueryName注解,解析出来对应的是ParameterHandler.QueryName()对象;
  • @QueryMap注解,解析出来对应的是ParameterHandler.QueryMap()对象;
  • @Header注解,解析出来对应的是ParameterHandler.Header()对象;
  • @HeaderMap注解,解析出来对应的是ParameterHandler.HeaderMap()对象;
  • @Field注解,解析出来对应的是ParameterHandler.Field()对象;
  • @FieldMap注解,解析出来对应的是ParameterHandler.FieldMap()对象;
  • @Part注解,解析出来对应的是ParameterHandler.Part()对象;
  • @PartMap注解,解析出来对应的是ParameterHandler.PartMap()对象;
  • @Body注解,解析出来对应的是ParameterHandler.Body()对象;
  • @Tag注解,解析出来对应的是ParameterHandler.Tag()对象;

这就是所有的方法参数注解类型。

RequestFactory.parseAnnotations()分析完了之后回到HttpServiceMethod.parseAnnotations()

HttpServiceMethod.parseAnnotations()

retrofit2.HttpServiceMethod#parseAnnotations

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    Retrofit retrofit, Method method, RequestFactory requestFactory) {
  boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
  boolean continuationWantsResponse = false;
  boolean continuationBodyNullable = false;
  boolean continuationIsUnit = false;

  Annotation[] annotations = method.getAnnotations();
  Type adapterType;
  if (isKotlinSuspendFunction) { ①
    Type[] parameterTypes = method.getGenericParameterTypes();
    Type responseType =
        Utils.getParameterLowerBound(
            0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
    if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
      // Unwrap the actual body type from Response<T>.
      responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
      continuationWantsResponse = true;
    } else {
      continuationIsUnit = Utils.isUnit(responseType);
    }

    adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
    annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
  } else {
    adapterType = method.getGenericReturnType(); ②
  }

  CallAdapter<ResponseT, ReturnT> callAdapter =
      createCallAdapter(retrofit, method, adapterType, annotations); ③
  Type responseType = callAdapter.responseType();
  if (responseType == okhttp3.Response.class) {
    throw methodError(
        method,
        "'"
            + getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
  }
  if (responseType == Response.class) {
    throw methodError(method, "Response must include generic type (e.g., Response<String>)");
  }
  // TODO support Unit for Kotlin?
  if (requestFactory.httpMethod.equals("HEAD")
      && !Void.class.equals(responseType)
      && !Utils.isUnit(responseType)) {
    throw methodError(method, "HEAD method must use Void or Unit as response type.");
  }

  Converter<ResponseBody, ResponseT> responseConverter =
      createResponseConverter(retrofit, method, responseType); ④

  okhttp3.Call.Factory callFactory = retrofit.callFactory; ⑤
  if (!isKotlinSuspendFunction) {
    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
  } else if (continuationWantsResponse) {
    //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
    return (HttpServiceMethod<ResponseT, ReturnT>)
        new SuspendForResponse<>(
            requestFactory,
            callFactory,
            responseConverter,
            (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter); ⑥
  } else {
    //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
    return (HttpServiceMethod<ResponseT, ReturnT>)
        new SuspendForBody<>(
            requestFactory,
            callFactory,
            responseConverter,
            (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
            continuationBodyNullable,
            continuationIsUnit); ⑦
  }
}
复制代码

这里面的逻辑还是略微比较多的,逐一看下:

  1. 第一步,判断是否是Kotlin的挂起函数,然后通过getGenericParameterTypes获取方法的返回类型;
  2. 第二步,如果不是Kotlin的挂起函数,直接通过getGenericReturnType获取方法的返回类型,这里的返回类型如果是泛型的话有可能拿不到具体的泛型值;
  3. 第三步创建CallAdapter,这里假设我们没有设置任何额外的CallAdapter,会使用默认的DefaultCallAdapter对象;
  4. 第四步创建ConverterAdapter,这里我们一般使用Gson来解析响应结果,转换成数据类,这边假设使用的是GsonResponseBodyConverter对象;
  5. 第五步仅仅是获取Retrofit对象中callFactory
  6. 第六步和第七步对Kotlin的挂起函数做判断,分析过程中默认非挂起函数,直接进入第七步,烦恼会一个CallAdapted对象。

到这我们发现Retrofit.create()方法中,动态代理中loadServiceMethod()方法最终返回的就是CallAdapted对象,loadServiceMethod()方法后面直接调用了invoke()方法,接下来进入CallAdapted.invoke()方法。

CallAdapter.invoke()

进入CallAdapted对象中,发现并没有invoke()方法的实现,只能退而求其次去看它的父类了:

retrofit2.HttpServiceMethod#invoke  

@Override
final @Nullable ReturnT invoke(Object[] args) {
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
  return adapt(call, args);
}
复制代码

invoke()方法中首先创建了一个OkHttpCall对象,然后调用adapt()方法。

OkhttpCallRetrofit用来管理发起网络调用和回调的具体类。

CallAdapted.adapt()

retrofit2.HttpServiceMethod.CallAdapted#adapt

@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
  // 这里的callAdapter为DefaultCallAdapterFactory中CallAdapter
  return callAdapter.adapt(call);
}

retrofit2.DefaultCallAdapterFactory#get

return new CallAdapter<Object, Call<?>>() {
  @Override
  public Type responseType() {
    return responseType;
  }

  @Override
  public Call<Object> adapt(Call<Object> call) {
    return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
  }
};
复制代码

adapt()方法直接调用callAdapter.adapt()方法,前面我们说到,默认使用CallAdapter,它是有DefaultCallAdapterFactory创建而成,CallAdapter.adapt()方法中只是创建了一个ExecutorCallbackCall对象。

到这里我们就清除了,在使用章节第四步生成的Call其实就是ExecutorCallbackCall对象,让他调用它的enqueue()方法就可以发起网络请求。

ExecutorCallbackCall.enqueue()

retrofit2.DefaultCallAdapterFactory.ExecutorCallbackCall#enqueue

ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
  this.callbackExecutor = callbackExecutor;
  this.delegate = delegate;
}

public void enqueue(final Callback<T> callback) {
  Objects.requireNonNull(callback, "callback == null");
  
  // 直接使用代理Call的enqueue方法,这里的delegate就是OkHttpCall对象
  delegate.enqueue(
      new Callback<T>() {
        @Override
        public void onResponse(Call<T> call, final Response<T> response) {
          // 主线程中回调结果,Android默认callbackExecutor为MainExecutor
          callbackExecutor.execute(
              () -> {
                if (delegate.isCanceled()) {
                  // Emulate OkHttp's behavior of throwing/delivering an IOException on
                  // cancellation.
                  callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                } else {
                  callback.onResponse(ExecutorCallbackCall.this, response);
                }
              });
        }

        @Override
        public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
        }
      });
}
复制代码

ExecutorCallbackCall.enqueue()方法很简单,直接调用delegate.enqueue(),然后将网络请求的结果回调到主线程中去处理。

这里的delegateCallAdapted.invoke()方法可以得知,它就是OkHttpCall对象,接着我们进入OkhttpCall.enqueue()方法。

OkHttpCall.enqueue()

retrofit2.OkHttpCall#enqueue

public void enqueue(final Callback<T> callback) {
  Objects.requireNonNull(callback, "callback == null");

  okhttp3.Call call;
  Throwable failure;

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

    call = rawCall;
    failure = creationFailure;
    if (call == null && failure == null) { ①
      try {
        call = rawCall = createRawCall(); ②
      } catch (Throwable t) {
        throwIfFatal(t);
        failure = creationFailure = t;
      }
    }
  }

  if (failure != null) {
    callback.onFailure(this, failure);
    return;
  }

  if (canceled) {
    call.cancel();
  }

  call.enqueue( ③
      new okhttp3.Callback() {
        @Override
        public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
          ...
        }

        @Override
        public void onFailure(okhttp3.Call call, IOException e) {
          ...
        }
      });
}
复制代码

此方法才是最终调用Okhttp3发起网络请求最终地点,重要的一共有三步:

  1. 第一步检查rawCall是否为空;
  2. In the second step, if it rawCallis empty, you need createRawCall()to create an OKhttp3.Callobject through it;
  3. Use Okhttp3.Call.enqueue()methods to initiate asynchronous requests.

So far, the Retrofitanalysis of the entire network request process is over, Retrofitand no network request operation is performed, but after all method annotations and parameter annotations are parsed, they are handed over Okhttp3to initiate specific network requests.

Guess you like

Origin juejin.im/post/7085725493562966046