preface

As an Android developer, I’m sure I have a pretty good grasp of retrofit, but what’s behind the implementation?

Using the process

  • 1. Create a RetroFit instance
  • 2. Create network request interface and configure corresponding method annotations and parameter annotations
  • 3. Realize network request interface instance through dynamic proxy
  • 4. Call the method of the network request interface instance to get the network executor
  • 5. A network request is made and the data returned by the server is parsed through the data converter
  • 6. Switch the data to the main thread through the callback executor and hand it to the user for processing

example

public static Retrofit create() { if (retrofit == null) { retrofit = new Retrofit.Builder().baseUrl("http://fanyi.youdao.com/").addConverterFactory(GsonConverterFactory.create()).build(); } return retrofit; } final RequestImpl request = retrofit.create(RequestImpl.class); Call<Translation> call = request.getCall(); call.enqueue(new Callback<Translation>() { @Override public void onResponse(Call<Translation> call, Response<Translation> response) { if (response ! = null && response.body() ! Println (" connection succeeded "+ Response.body ().toString()); The e (" Retrofit ", "the connection is successful" + response. The body (). The toString ()); }} @override public void onFailure(Call<Translation> Call, Throwable t) {system.out.println (" connection failed "); Log.e("Retrofit"," failed connection "); }});Copy the code

Source code analysis

1. Create a RetroFit instance

Step 1

A builder pattern is used here

public Builder() { this(Platform.get()); } -------------- //1. Private static final Platform Platform = findPlatform(); static Platform get() { return PLATFORM; } private static Platform findPlatform() { try { Class.forName("android.os.Build"); if (Build.VERSION.SDK_INT ! = 0) { return new Android(); } } catch (ClassNotFoundException ignored) { } try { Class.forName("java.util.Optional"); return new Java8(); } catch (ClassNotFoundException ignored) { } try { Class.forName("org.robovm.apple.foundation.NSObject"); return new IOS(); } catch (ClassNotFoundException ignored) { } return new Platform(); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - / / 2. Public Static Final Class Builder {// RetroFIT supports a platform  Platform platform; // The Factory function of network requests is to produce network requester (call). The default is okhttp private okHttp3.call.factory callFactory; Private HttpUrl baseUrl; Private List<Converter.Factory> converterFactories = new ArrayList<>(); Private List< callAdapter.factory > adapterFactories = new ArrayList<>(); // The method Executor that executes the callback private Executor callbackExecutor; // Tokens private Boolean WAITS; Builder(Platform platform) { this.platform = platform; // Add the built-in converter factory first. This prevents overriding its behavior but also // ensures correct behavior // Add a default converterFactories. Add (new BuiltInConverters()); }Copy the code

Step 2

public Builder baseUrl(String baseUrl) { checkNotNull(baseUrl, "baseUrl == null"); HttpUrl = httpurl.parse (baseUrl); // HttpUrl = httpurl.parse (baseUrl); If (httpUrl == null) {throw new IllegalArgumentException("Illegal URL: " + baseUrl); } return baseUrl(httpUrl); }Copy the code

Step 3

addConverterFactory(GsonConverterFactory.create())

See first GsonConverterFactory. The create ()The abstract Factory pattern is used here (related design patterns include simple Factory pattern, factory Method pattern)

Public Builder addConverterFactory(ConverterFactory) {public Builder addConverterFactory(ConverterFactory) { converterFactories.add(checkNotNull(factory, "factory == null")); return this; }Copy the code

Step 4

public Retrofit build() { if (baseUrl == null) { throw new IllegalStateException("Base URL required."); } // If the network request Factory is not set manually, okhttp is provided by default for network requests. if (callFactory == null) { callFactory = new OkHttpClient(); } // If no callback method executor is provided here, If the platform is used by default, the default callbackExecutor is provided. For example, the Android platform provides the MainThreadExecutor callbackExecutor = this.callbackExecutor; if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); // Make a defensive copy of the adapters and add the default Call adapter. // Add the manually configured network request adapter factory to the network request adapter factory set // Request adapter factory sets to be stored in order: custom 1 adaptation factory, custom 2 adaptation factory..... // Finally add a default adapter Factory List< calladapterFactory > adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); // these converters will be converted with any of their properties. // These converters will be converted with any of their properties. List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories); // Finally return a Retrofit object, Return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); }Copy the code

The first stage summary

Retrofit uses the Builder pattern to create an instance of Retrofit through the Builder class

2. Create a network request interface and instance

<-- step 1: define the class that receives network data --> <-- javabean.java --> public class javabeans {.. } <-- step 2: define the network request interface class --> <-- AccessApi. Java --> public interface AccessApi {// Retrofit divides the URL of a web request into two parts: 1) The baseurL is set when the Retrofit object is created; The other part is set in the web request interface (i.e., here) // If the URL in the interface is a full WEB address, then the part you set when creating Retrofit objects can be set without @get ("openapi.do? Keyfrom =Yanzhikai&key=2032414398&type= data&docType =json&version=1.1&q=car") // The return type is Call<*>, * is the parsed data type, i.e. JavaBean}Copy the code

Creating an Interface Instance

AccessApi NetService = retrofit.create(AccessApi.class);
Copy the code

Source code analysis

Public < T > T create (final Class < T > service) {/ / this Class is an interface first, then the interface can't inherit other interfaces Utils. ValidateServiceInterface (service); // Whether to verify methods in this class in advance and configure them into ServiceMethod objects if (validatembit/s) {eagerlyValidateMethods(service); } // dynamic proxy mode is used to create the network request interface instance // service.getclassloader (), // dynamically generated interface implementation Class // new Class<? >[] { service }, // New InvocationHandler() {// Pass the implementation of the proxy class to the InvocationHandler class as the concrete implementation of return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<? >[] { service }, new InvocationHandler() { private final Platform platform = Platform.get(); // In addition to executing the method corresponding to the interface instance, Override Public Object Invoke (Object proxy, Method Method, Object... args) throws Throwable { // If the method is a method from Object then defer to normal invocation. if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } // Core method // Core method 1 Read the methods in the network request interface and configure the ServiceMethod object based on the previously configured properties. OkHttpCall OkHttpCall = new OkHttpCall<>(ServiceMethod, args); / / 3 / / core method according to okHttpCall return Call return serviceMethod. CallAdapter. Adapt (okHttpCall); }}); }Copy the code

Core Approach 1

  ServiceMethod loadServiceMethod(Method method) {
    ServiceMethod result;
    //设置线程同步锁
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      //先判断缓存
      if (result == null) {
      //不存在则去创建对象的ServiceMethod 并放到缓存中
        result = new ServiceMethod.Builder(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }
  
  ---------------------------
  
  //new ServiceMethod.Builder(this, method).build();分析
  //ServiceMethod包含的成员变量
  final class ServiceMethod<T> {
  // Upper and lower characters, digits, underscores, and hyphens, starting with a character.
  static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*";
  static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}");
  static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM);

  final okhttp3.Call.Factory callFactory;
  final CallAdapter<?> callAdapter;

  private final HttpUrl baseUrl;
  private final Converter<ResponseBody, T> responseConverter;
  private final String httpMethod;
  private final String relativeUrl;
  private final Headers headers; //网络请求的http请求头 键值对  
  private final MediaType contentType;   // 网络请求的http报文body的类型 
  private final boolean hasBody;
  private final boolean isFormEncoded;
  private final boolean isMultipart;
  // 方法参数处理器
  // 作用:负责解析 API 定义时每个方法的参数,并在构造 HTTP 请求时设置参数;
  private final ParameterHandler<?>[] parameterHandlers;
  
  -----------------------------
  public Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;

      // 获取网络请求接口方法里的注释
      this.methodAnnotations = method.getAnnotations();
      // 获取网络请求接口方法里的参数类型		
      this.parameterTypes = method.getGenericParameterTypes();	
      //获取网络请求接口方法里的注解内容	
      this.parameterAnnotationsArray = method.getParameterAnnotations();	
    }
    
 ------------------------
 // 作用:控制ServiceMethod对象的生成流程

 public ServiceMethod build() {

      callAdapter = createCallAdapter();	
      // 根据网络请求接口方法的返回值和注解类型,从Retrofit对象中获取对应的网络请求适配器  -->关注点1
     
      responseType = callAdapter.responseType();	
     // 根据网络请求接口方法的返回值和注解类型,从Retrofit对象中获取该网络适配器返回的数据类型
     
      responseConverter = createResponseConverter();	
      // 根据网络请求接口方法的返回值和注解类型,从Retrofit对象中获取对应的数据转换器  -->关注点3
      // 构造 HTTP 请求时,我们传递的参数都是String
      // Retrofit 类提供 converter把传递的参数都转化为 String 
      // 其余类型的参数都利用 Converter.Factory 的stringConverter 进行转换
      // @Body 和 @Part 类型的参数利用Converter.Factory 提供的 requestBodyConverter 进行转换
      // 这三种 converter 都是通过“询问”工厂列表进行提供,而工厂列表我们可以在构造 Retrofit 对象时进行添加。
      
       
       for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
      // 解析网络请求接口中方法的注解
      // 主要是解析获取Http请求的方法
     // 注解包括:DELETE、GET、POST、HEAD、PATCH、PUT、OPTIONS、HTTP、retrofit2.http.Headers、Multipart、FormUrlEncoded
     // 处理主要是调用方法 parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) ServiceMethod中的httpMethod、hasBody、relativeUrl、relativeUrlParamNames域进行赋值
      
     int parameterCount = parameterAnnotationsArray.length;
     // 获取当前方法的参数数量
      
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        Type parameterType = parameterTypes[p];
        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
	    // 为方法中的每个参数创建一个ParameterHandler<?>对象并解析每个参数使用的注解类型
        // 该对象的创建过程就是对方法参数中注解进行解析
        // 这里的注解包括:Body、PartMap、Part、FieldMap、Field、Header、QueryMap、Query、Path、Url 
        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      } 
      return new ServiceMethod<>(this);

<-- 关注点1:createCallAdapter() -->
 private CallAdapter<?> createCallAdapter() {

      // 获取网络请求接口里方法的返回值类型
      Type returnType = method.getGenericReturnType();		

      // 获取网络请求接口接口里的注解
      // 此处使用的是@Get
      Annotation[] annotations = method.getAnnotations();		
      try {

      return retrofit.callAdapter(returnType, annotations);	
      // 根据网络请求接口方法的返回值和注解类型,从Retrofit对象中获取对应的网络请求适配器
      // 下面会详细说明retrofit.callAdapter() -- >关注点2
      }
...


<-- 关注点2:retrofit.callAdapter()  -->
 public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }

 public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {

    // 创建 CallAdapter 如下
    // 遍历 CallAdapter.Factory 集合寻找合适的工厂(该工厂集合在第一步构造 Retrofit 对象时进行添加(第一步时已经说明))
    // 如果最终没有工厂提供需要的 CallAdapter,将抛出异常
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);		
      if (adapter != null) {
        return adapter;
      }
    }


<--   关注点3:createResponseConverter() -->

 private Converter<ResponseBody, T> createResponseConverter() {
      Annotation[] annotations = method.getAnnotations();
      try {
	
        // responseConverter 还是由 Retrofit 类提供  -->关注点4
        return retrofit.responseBodyConverter(responseType, annotations);
      } catch (RuntimeException e) { 
        throw methodError(e, "Unable to create converter for %s", responseType);
      }
    }

<--   关注点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++) {

       // 获取Converter 过程:(和获取 callAdapter 基本一致)
         Converter<ResponseBody, ?> converter =
          converterFactories.get(i).responseBodyConverter(type, annotations, this);	
       // 遍历 Converter.Factory 集合并寻找合适的工厂(该工厂集合在构造 Retrofit 对象时进行添加(第一步时已经说明))
       // 由于构造Retroifit采用的是Gson解析方式,所以取出的是GsonResponseBodyConverter
       // Retrofit - Converters 还提供了 JSON,XML,ProtoBuf 等类型数据的转换功能。
       // 继续看responseBodyConverter() -->关注点5	
    }


<--   关注点5:responseBodyConverter() -->
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, 
    Annotation[] annotations, Retrofit retrofit) {

  
  TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
  // 根据目标类型,利用 Gson#getAdapter 获取相应的 adapter
  return new GsonResponseBodyConverter<>(gson, adapter);
}

// 做数据转换时调用 Gson 的 API 即可。
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 {
      return adapter.read(jsonReader);
    } finally {
      value.close();
    }
  }
}
Copy the code

OkHttpCall OkHttpCall = new OkHttpCall<>(serviceMethod, args);

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

The core method 3 return serviceMethod. CallAdapter. Adapt (okHttpCall);

ExecutorCallbackCall<T> implements Call<T> static final class callbackCall <T> implements call <T> { final Executor callbackExecutor; final Call<T> delegate; ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) { this.callbackExecutor = callbackExecutor; this.delegate = delegate; } public void enqueue(final Callback<T> callback) { if (callback == null) { throw new NullPointerException("callback == null"); } else {this.delegate. Enqueue (new Callback<T>() {public void onResponse(Call<T> Call, Final Response<T> Response) {//callbackExecutor, as I mentioned earlier, executes data by calling back to the main thread ExecutorCallbackCall.this.callbackExecutor.execute(new Runnable() { public void run() { if (ExecutorCallbackCall.this.delegate.isCanceled()) { callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled")); } else { callback.onResponse(ExecutorCallbackCall.this, response); }}}); } public void onFailure(Call<T> call, final Throwable t) { ExecutorCallbackCall.this.callbackExecutor.execute(new Runnable() { public void run() { callback.onFailure(ExecutorCallbackCall.this, t); }}); }}); }}Copy the code

Invoke the relevant request method through the network interface request instance

Call call = NetService.getCall(); The OkHttpCall class is a wrapper around OkHttp. A Call object of type OkHttpCall cannot send a network Request. A Request object must be created to send a network Request

Perform network request synchronization asynchronism

asynchronous

call.enqueue(new Callback<Translation>() { @Override public void onResponse(Call<Translation> call, Response<Translation> response) { if (response ! = null && response.body() ! Println (" connection succeeded "+ Response.body ().toString()); The e (" Retrofit ", "the connection is successful" + response. The body (). The toString ()); }} @override public void onFailure(Call<Translation> Call, Throwable t) {system.out.println (" connection failed "); Log.e("Retrofit"," failed connection "); }}); ------------------------- static final class ExecutorCallbackCall<T> implements Call<T> { final Executor callbackExecutor; final Call<T> delegate; ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) { this.callbackExecutor = callbackExecutor; //delegate is the previous wrapper class OkHttpCall this.delegate = delegate; } @Override public void enqueue(final Callback<T> callback) { if (callback == null) throw new NullPointerException("callback == null"); delegate.enqueue(new Callback<T>() { @Override public void onResponse(Call<T> call, Final Response<T> Response) {//callbackExecutor is a callbackExecutor that is called back to the main thread callbackexecutor.execute (new) after executing the okhttpcall onresponse internally Runnable() { @Override public void run() { 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(new Runnable() { @Override public void run() { callback.onFailure(ExecutorCallbackCall.this, t); }}); }}); }Copy the code

The delegate is okhttpCall delegate. The enqueue (XXX)

@Override public void enqueue(final Callback<T> callback) { if (callback == null) throw new NullPointerException("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 {// get okHttp3.Call call = rawCall = createRawCall(); // step 1} catch (Throwable 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) throws IOException { Response<T> response; try { response = parseResponse(rawResponse); } catch (Throwable e) { callFailure(e); return; } callSuccess(response); Call Private okHttp3. Call createRawCall() throws IOException {Request Request = serviceMethod.toRequest(args); okhttp3.Call call = serviceMethod.callFactory.newCall(request); if (call == null) { throw new NullPointerException("Call.Factory returned null."); } return call; }Copy the code

Synchronization is slightly off

conclusion

At this point, we’ve analyzed retrofit’s request process from the code level, and it’s basically an encapsulation of OKHTTP. The final implementation is still okHTTP, so why not just use OKHTTP? Reference 1 Reference 2

extension

What are retrofit’s benefits over OKHTTP? Pros and cons of Retrofit pros and cons of OKHTTP

The okHTTP callback runs in the child thread, so you need to manually call back to the main thread by hanlder. Retrofit also needs to add base classes for different request methods such as POST and GET. After all, retrofit is just a encapsulation of OKHTTP. If the network layer was changed to Volley, then all network requests would need to be changed. However, Retrofit can keep the existing logic code and only need to change one network request library at the bottom

The disadvantages of Volley

Volley is bad for downloading large files through bytes, while OKhttp is bad for downloading large files through streams

Retrofit is all about learning

Annotations (run-time compile-time), reflection, dynamic proxy, builder mode Hander principle (all need to be connected later) (dynamic proxy and annotations relationship)

Retrofit interview Site process (follow-up)