Retrofit 原始碼學習與使用

浩宇碧海發表於2018-06-25

預習

本文主要對retrofit的原始碼進行學習,瞭解,學習其設計模式,與實現方式。

retrofit 的基本用法:

  1. Retrofit turns your HTTP API into a Java interface.
    (將HTTP API轉換為Java介面。)
public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}
複製程式碼
  1. The Retrofit class generates an implementation of the GitHubService interface.
    (Retrofit類生成GitHubService介面的實現。)
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

GitHubService service = retrofit.create(GitHubService.class);
複製程式碼
  1. Each Call from the created GitHubService can make a synchronous or asynchronous HTTP request to the remote webserver.
    (建立的GitHubService的每個呼叫都可以向遠端web伺服器發出同步或非同步的HTTP請求。)
Call<List<Repo>> repos = service.listRepos("octocat");
複製程式碼



A : service介面的構建

Retrofit 原始碼學習與使用

  1. 首先建立一個service的介面類。
  2. 指定請求方式 常見的有GET,POST,PUT,DELETE...,注意大小寫。
  3. 配置請求地址,注意path的配置規則(見retrofit 使用教程的7.2)
BaseUrl 和URL有關的註解中提供的值 最後結果
http://localhost:4567/path/to/other/ /post http://localhost:4567/post
http://localhost:4567/path/to/other/ post http://localhost:4567/path/to/other/post
http://localhost:4567/path/to/other/ github.com/ikidou github.com/ikidou
  • 如果你在註解中提供的url是完整的url,則url將作為請求的url。
  • 如果你在註解中提供的url是不完整的url,且不以 / 開頭,則請求的url為baseUrl+註解中提供的值
  • 如果你在註解中提供的url是不完整的url,且以 / 開頭,則請求的url為baseUrl的主機部分+註解中提供的值
  1. 指定請求的回撥物件,預設返回retrofit的Call<T>物件,這裡我們可以
  • 通過實現retrofit的CallAdapter<R, T> 介面,來自定義我們的返回物件。
  • 再繼承CallAdapter.Factory生成我們自己的Factory,在get()方法返回我們自定義的CallAdapter。
  • 最後呼叫Retrofit.Builder的addCallAdapterFactory(Factory)
  1. 指定請求結束後我們要接收的型別,如:okHttp返回預設的是ResponseBody,通過Retrofit的Converter可以轉換為我們需要的 string 或 反序列化為java Bean物件等。
  • 定義一個MyConvert類實現retrofit的Converter介面。
  • 定義一個MyConvertFactory繼承retrofit的Converter.Factory,實現responseBodyConverter()返回我們自定義的MyConvert類。
  • 使用Retrofit.Builder.addConverterFactory(new MyConvertFactory())新增我們自定義的Factory.
  1. 定義我們請求的方法名字。
  2. 請求引數的註解 見(見retrofit 使用教程第三類:引數類

Retrofit 原始碼學習與使用

  1. 請求引數的名字
  2. 引數型別
  3. 引數名稱



B : retrofit的構建,原始碼分析

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();
複製程式碼

我們可以看到retrofit的構建使用了builder模式。

1. 看一下Retrofit.Builder類:

/**
   * Build a new {@link Retrofit}.
   * <p>
   * Calling {@link #baseUrl} is required before calling {@link #build()}. All other methods
   * are optional.
   */
 public static final class Builder {
    private final Platform platform;
    private @Nullable okhttp3.Call.Factory callFactory;
    private HttpUrl baseUrl;
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
    private @Nullable Executor callbackExecutor;
    private boolean validateEagerly;
    ...
    }
複製程式碼

從原始碼可以看到:

  • 從註釋看到baseUrl()必須在build()之前呼叫,其它可以隨意。
  • platform 指定呼叫的平臺,這裡我們使用的是Android.
  • callFactory okhttp3的Call.Factory
  • baseUrl 請求的root地址
  • converterFactories 儲存我們自定義的和系統預設提供的convertFactory見上面的A.5
  • adapterFactories 儲存我們自定義的和系統預設提供的adapterFactory 見上面的A.4
  • callbackExecutor 執行緒呼叫類
  • validateEagerly 為true時做一些預處理操作

2. build()方法的實現

  /**
     * Create the {@link Retrofit} instance using the configured values.
     * <p>
     * Note: If neither {@link #client} nor {@link #callFactory} is called a default {@link
     * OkHttpClient} will be created and used.
     */
     //通過配置引數建立一個Retrofit的例項,如果沒有配置HttpClient也沒有配置CallFactory將採用預設的OkHttpClient
    public Retrofit build() {

      if (baseUrl == null) {
      // baseUrl不能為空
        throw new IllegalStateException("Base URL required.");
      }
        //配置HttpClient,可以看出retrofit預設採用OkHttpClient
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
        //指定callback回撥方法的執行器,這裡的platform是Android平臺,可以看出預設下面的Android--Platform的MainThreadExecutor
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      //將我們自定義的adapterFactory新增到集合中
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // Make a defensive copy of the converters.
      //將我們自定義的convertFactory新增到集合中
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
       // 返回Retrofit
      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }
複製程式碼

Android--Platform

static class Android extends Platform {
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }

    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }
  }
複製程式碼

經過build()操作後,我們配置好了一個我們想要的retrofit,接下來我們來看看這些配置是如何生效的,我們通過

GitHubService service = retrofit.create(GitHubService.class);
複製程式碼

來生成我們service介面的實現類。我們來看create()方法。

3. retrofit.create()

 public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, @Nullable 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);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }
複製程式碼
  • validateEagerly 為true時會走 eagerlyValidateMethods(service)
  private void eagerlyValidateMethods(Class<?> service) {
    Platform platform = Platform.get();
    for (Method method : service.getDeclaredMethods()) {
      if (!platform.isDefaultMethod(method)) {
        loadServiceMethod(method);
      }
    }
  }

  ServiceMethod<?, ?> loadServiceMethod(Method method) {
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }
複製程式碼

這裡我們看到,是對ServiceMethod做的快取處理。如果快取中存在直接返回。

  • 接下來我們看見採用了動態代理的模式來建立service介面的實現類。
  • 如果是platform的方法直接return
  • 如果是我們service介面內的方法,我們看到通過上面的loadServiceMethod(method)來建立了一個ServiceMethod類,我們來看ServiceMethod類做了那些處理。

4. ServiceMethod

我們看到ServiceMethod也是採用Builder模式來構建。

/** Adapts an invocation of an interface method into an HTTP call. */
final class ServiceMethod<R, 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;//okhttp3.Call.Factory
  final CallAdapter<R, T> callAdapter;//返回物件介面卡
  private final HttpUrl baseUrl;//根地址
  private final Converter<ResponseBody, R> responseConverter;//reponseBody轉換類
  private final String httpMethod;//請求方式,PUT,POST...
  private final String relativeUrl;//相對地址
  private final Headers headers;//請求頭
  private final MediaType contentType;//請求的Content-Type
  private final boolean hasBody;//是否有請求訊息體
  private final boolean isFormEncoded;//是否進行引數編碼
  private final boolean isMultipart;// 上傳時使用
  private final ParameterHandler<?>[] parameterHandlers;//請求引數註解的封裝物件

  ServiceMethod(Builder<R, T> builder) {
    this.callFactory = builder.retrofit.callFactory();
    this.callAdapter = builder.callAdapter;
    this.baseUrl = builder.retrofit.baseUrl();
    this.responseConverter = builder.responseConverter;
    this.httpMethod = builder.httpMethod;
    this.relativeUrl = builder.relativeUrl;
    this.headers = builder.headers;
    this.contentType = builder.contentType;
    this.hasBody = builder.hasBody;
    this.isFormEncoded = builder.isFormEncoded;
    this.isMultipart = builder.isMultipart;
    this.parameterHandlers = builder.parameterHandlers;
  }
  ...
}
複製程式碼

接著來看 build() 方法

    public ServiceMethod build() {
       // [1] 建立CallAdapter,並得到返回的物件
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      ...
      //[2] 建立ResponseConverte,對返回的資料進行處理
      responseConverter = createResponseConverter();
      //[3]遍歷方法註解,獲取具體的配置引數
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
        ...
        //[4] 方法中引數註解的處理
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        Type parameterType = parameterTypes[p];
        ...
        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
       ...
        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }

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

      return new ServiceMethod<>(this);
    }
複製程式碼

上面的ServiceMethod.build()方法大概進行了如下操作:

  • [1] 建立CallAdapter,並得到返回的物件
  • [2] 建立ResponseConverte,對返回的資料進行處理
  • [3] 遍歷方法註解,獲取具體的配置引數
  • [4] 方法中引數註解的處理

接下來我們具體看看是如何實現的

[1] createCallAdapter()

 private CallAdapter<T, R> createCallAdapter() {
        //獲取方法的完整資訊的返回值
      Type returnType = method.getGenericReturnType();
      ...
      //獲得註解
      Annotation[] annotations = method.getAnnotations();
      ...
      return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
      ...
    }
複製程式碼

可以看到實際上是呼叫了retrofit.callAdapter(returnType, annotations)方法

 public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }

  public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    ...
    int start = adapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
        ...
  }
複製程式碼

從上面最終呼叫的nextCallAdapter方法,adapterFactories.get(i).get(returnType, annotations, this)看到,最終返回我們新增到adapterFactories的CallAdapterFactory.get()物件就是我們自定義的CallAdapter,注意這裡是有排序概念的,排在前面的優先匹配。

再通過responseType = callAdapter.responseType();將我們自己定義的call物件,或Obserable物件,或其他需要型別返回,見上面圖A.④。

[2] createResponseConverter()

 private Converter<ResponseBody, T> createResponseConverter() {
      Annotation[] annotations = method.getAnnotations();
      try {
        return retrofit.responseBodyConverter(responseType, annotations);
      } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(e, "Unable to create converter for %s", responseType);
      }
    }
複製程式碼

再看retrofit.responseBodyConverter(responseType, annotations)

 public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
    return nextResponseBodyConverter(null, type, annotations);
  }

  public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
      @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
        ...
    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
      Converter<ResponseBody, ?> converter =
          converterFactories.get(i).responseBodyConverter(type, annotations, this);
      if (converter != null) {
        //noinspection unchecked
        return (Converter<ResponseBody, T>) converter;
      }
    }
    ...
  }
複製程式碼

這裡我們最終看到通過converterFactories.get(i).responseBodyConverter(type, annotations, this)方法,返回我們自定義的泛型物件見上面圖A.⑤,通常是我們反序列化操作轉換後的物件,或根據需要過濾一些資料,返回有用的資料。

[3] parseMethodAnnotation(annotation)

在這裡處理方法上面的註解,獲取到請求方式見上面圖A.②,並對不同的請求方式做相應處理。

  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);
        if (!Void.class.equals(responseType)) {
          throw methodError("HEAD method must use Void as response type.");
        }
      } 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("@Headers annotation is empty.");
        }
        headers = parseHeaders(headersToParse);
      } else if (annotation instanceof Multipart) {
        if (isFormEncoded) {
          throw methodError("Only one encoding annotation is allowed.");
        }
        isMultipart = true;
      } else if (annotation instanceof FormUrlEncoded) {
        if (isMultipart) {
          throw methodError("Only one encoding annotation is allowed.");
        }
        isFormEncoded = true;
      }
    }
複製程式碼

接下來看一下 parseHttpMethodAndPath()

private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
      ...
      this.httpMethod = httpMethod;
      this.hasBody = hasBody;

      if (value.isEmpty()) {
        return;
      }

      // Get the relative URL path and existing query string, if present.
      int question = value.indexOf('?');
      if (question != -1 && question < value.length() - 1) {
        // Ensure the query string does not have any named parameters.
        String queryParams = value.substring(question + 1);
        Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
        if (queryParamMatcher.find()) {
          throw methodError("URL query string \"%s\" must not have replace block. "
              + "For dynamic query parameters use @Query.", queryParams);
        }
      }
      this.relativeUrl = value;
      this.relativeUrlParamNames = parsePathParameters(value);
    }

複製程式碼

我們看到這裡將獲取到的請求方式httpMethod,是否有hasBody,還有地址路徑,headers,還有地址中含有的請求引數,等等,都賦值給ServiceMethod.Builder的相應屬性。

[4] parameterHandlers[]的賦值操作

在ServiceMethod.Build(Retrofit retrofit, Method method)時我們看一下

  Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }
複製程式碼
  • methodAnnotations 獲得method上的所有註解
  • parameterTypes 獲得所有的引數型別,String ,int ...
  • parameterAnnotationsArray 獲得引數的註解集合

再看

int parameterCount = parameterAnnotationsArray.length;
        //構建一個ParameterHandler集合,
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
      //獲取第p個引數型別
        Type parameterType = parameterTypes[p];
        ...
        //獲取第p個引數註解集合
        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
       ...
       //構建一個ParameterHandler物件
        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }

複製程式碼

看看 parseParameter(p, parameterType, parameterAnnotations)

  private ParameterHandler<?> parseParameter(
        int p, Type parameterType, Annotation[] annotations) {
      ParameterHandler<?> result = null;
      for (Annotation annotation : annotations) {
      
        ParameterHandler<?> annotationAction = parseParameterAnnotation(p, parameterType, annotations,annotation);
        
        }
            ...

      return result;
    }
    
複製程式碼

接著往下看parseParameterAnnotation(p, parameterType, annotations,annotation)

private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
      if (annotation instanceof Url) {
        if (gotUrl) {
          throw parameterError(p, "Multiple @Url method annotations found.");
        }
        if (gotPath) {
          throw parameterError(p, "@Path parameters may not be used with @Url.");
        }
        if (gotQuery) {
          throw parameterError(p, "A @Url parameter must not come after a @Query");
        }
        if (relativeUrl != null) {
          throw parameterError(p, "@Url cannot be used with @%s URL", httpMethod);
        }

        gotUrl = true;

        if (type == HttpUrl.class
            || type == String.class
            || type == URI.class
            || (type instanceof Class && "android.net.Uri".equals(((Class<?>) type).getName()))) {
          return new ParameterHandler.RelativeUrl();
        } else {
          throw parameterError(p,
              "@Url must be okhttp3.HttpUrl, String, java.net.URI, or android.net.Uri type.");
        }

      } else if (annotation instanceof Path) {
        if (gotQuery) {
          throw parameterError(p, "A @Path parameter must not come after a @Query.");
        }
        if (gotUrl) {
          throw parameterError(p, "@Path parameters may not be used with @Url.");
        }
        if (relativeUrl == null) {
          throw parameterError(p, "@Path can only be used with relative url on @%s", httpMethod);
        }
        gotPath = true;

        Path path = (Path) annotation;
        String name = path.value();
        validatePathName(p, name);

        Converter<?, String> converter = retrofit.stringConverter(type, annotations);
        return new ParameterHandler.Path<>(name, converter, path.encoded());

      } else if (annotation instanceof Query) {
        Query query = (Query) annotation;
        String name = query.value();
        boolean encoded = query.encoded();

        Class<?> rawParameterType = Utils.getRawType(type);
        gotQuery = true;
        if (Iterable.class.isAssignableFrom(rawParameterType)) {
          if (!(type instanceof ParameterizedType)) {
            throw parameterError(p, rawParameterType.getSimpleName()
                + " must include generic type (e.g., "
                + rawParameterType.getSimpleName()
                + "<String>)");
          }
          ParameterizedType parameterizedType = (ParameterizedType) type;
          Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
          Converter<?, String> converter =
              retrofit.stringConverter(iterableType, annotations);
          return new ParameterHandler.Query<>(name, converter, encoded).iterable();
        } else if (rawParameterType.isArray()) {
          Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
          Converter<?, String> converter =
              retrofit.stringConverter(arrayComponentType, annotations);
          return new ParameterHandler.Query<>(name, converter, encoded).array();
        } else {
          Converter<?, String> converter =
              retrofit.stringConverter(type, annotations);
          return new ParameterHandler.Query<>(name, converter, encoded);
        }

      } else if (annotation instanceof QueryName) {
        QueryName query = (QueryName) annotation;
        boolean encoded = query.encoded();

        Class<?> rawParameterType = Utils.getRawType(type);
        gotQuery = true;
        if (Iterable.class.isAssignableFrom(rawParameterType)) {
          if (!(type instanceof ParameterizedType)) {
            throw parameterError(p, rawParameterType.getSimpleName()
                + " must include generic type (e.g., "
                + rawParameterType.getSimpleName()
                + "<String>)");
          }
          ParameterizedType parameterizedType = (ParameterizedType) type;
          Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
          Converter<?, String> converter =
              retrofit.stringConverter(iterableType, annotations);
          return new ParameterHandler.QueryName<>(converter, encoded).iterable();
        } else if (rawParameterType.isArray()) {
          Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
          Converter<?, String> converter =
              retrofit.stringConverter(arrayComponentType, annotations);
          return new ParameterHandler.QueryName<>(converter, encoded).array();
        } else {
          Converter<?, String> converter =
              retrofit.stringConverter(type, annotations);
          return new ParameterHandler.QueryName<>(converter, encoded);
        }

      } else if (annotation instanceof QueryMap) {
        Class<?> rawParameterType = Utils.getRawType(type);
        if (!Map.class.isAssignableFrom(rawParameterType)) {
          throw parameterError(p, "@QueryMap parameter type must be Map.");
        }
        Type mapType = Utils.getSupertype(type, rawParameterType, Map.class);
        if (!(mapType instanceof ParameterizedType)) {
          throw parameterError(p, "Map must include generic types (e.g., Map<String, String>)");
        }
        ParameterizedType parameterizedType = (ParameterizedType) mapType;
        Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
        if (String.class != keyType) {
          throw parameterError(p, "@QueryMap keys must be of type String: " + keyType);
        }
        Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
        Converter<?, String> valueConverter =
            retrofit.stringConverter(valueType, annotations);

        return new ParameterHandler.QueryMap<>(valueConverter, ((QueryMap) annotation).encoded());

      } else if (annotation instanceof Header) {
        Header header = (Header) annotation;
        String name = header.value();

        Class<?> rawParameterType = Utils.getRawType(type);
        if (Iterable.class.isAssignableFrom(rawParameterType)) {
          if (!(type instanceof ParameterizedType)) {
            throw parameterError(p, rawParameterType.getSimpleName()
                + " must include generic type (e.g., "
                + rawParameterType.getSimpleName()
                + "<String>)");
          }
          ParameterizedType parameterizedType = (ParameterizedType) type;
          Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
          Converter<?, String> converter =
              retrofit.stringConverter(iterableType, annotations);
          return new ParameterHandler.Header<>(name, converter).iterable();
        } else if (rawParameterType.isArray()) {
          Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
          Converter<?, String> converter =
              retrofit.stringConverter(arrayComponentType, annotations);
          return new ParameterHandler.Header<>(name, converter).array();
        } else {
          Converter<?, String> converter =
              retrofit.stringConverter(type, annotations);
          return new ParameterHandler.Header<>(name, converter);
        }

      } else if (annotation instanceof HeaderMap) {
        Class<?> rawParameterType = Utils.getRawType(type);
        if (!Map.class.isAssignableFrom(rawParameterType)) {
          throw parameterError(p, "@HeaderMap parameter type must be Map.");
        }
        Type mapType = Utils.getSupertype(type, rawParameterType, Map.class);
        if (!(mapType instanceof ParameterizedType)) {
          throw parameterError(p, "Map must include generic types (e.g., Map<String, String>)");
        }
        ParameterizedType parameterizedType = (ParameterizedType) mapType;
        Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
        if (String.class != keyType) {
          throw parameterError(p, "@HeaderMap keys must be of type String: " + keyType);
        }
        Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
        Converter<?, String> valueConverter =
            retrofit.stringConverter(valueType, annotations);

        return new ParameterHandler.HeaderMap<>(valueConverter);

      } else if (annotation instanceof Field) {
        if (!isFormEncoded) {
          throw parameterError(p, "@Field parameters can only be used with form encoding.");
        }
        Field field = (Field) annotation;
        String name = field.value();
        boolean encoded = field.encoded();

        gotField = true;

        Class<?> rawParameterType = Utils.getRawType(type);
        if (Iterable.class.isAssignableFrom(rawParameterType)) {
          if (!(type instanceof ParameterizedType)) {
            throw parameterError(p, rawParameterType.getSimpleName()
                + " must include generic type (e.g., "
                + rawParameterType.getSimpleName()
                + "<String>)");
          }
          ParameterizedType parameterizedType = (ParameterizedType) type;
          Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
          Converter<?, String> converter =
              retrofit.stringConverter(iterableType, annotations);
          return new ParameterHandler.Field<>(name, converter, encoded).iterable();
        } else if (rawParameterType.isArray()) {
          Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
          Converter<?, String> converter =
              retrofit.stringConverter(arrayComponentType, annotations);
          return new ParameterHandler.Field<>(name, converter, encoded).array();
        } else {
          Converter<?, String> converter =
              retrofit.stringConverter(type, annotations);
          return new ParameterHandler.Field<>(name, converter, encoded);
        }

      } else if (annotation instanceof FieldMap) {
        if (!isFormEncoded) {
          throw parameterError(p, "@FieldMap parameters can only be used with form encoding.");
        }
        Class<?> rawParameterType = Utils.getRawType(type);
        if (!Map.class.isAssignableFrom(rawParameterType)) {
          throw parameterError(p, "@FieldMap parameter type must be Map.");
        }
        Type mapType = Utils.getSupertype(type, rawParameterType, Map.class);
        if (!(mapType instanceof ParameterizedType)) {
          throw parameterError(p,
              "Map must include generic types (e.g., Map<String, String>)");
        }
        ParameterizedType parameterizedType = (ParameterizedType) mapType;
        Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
        if (String.class != keyType) {
          throw parameterError(p, "@FieldMap keys must be of type String: " + keyType);
        }
        Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
        Converter<?, String> valueConverter =
            retrofit.stringConverter(valueType, annotations);

        gotField = true;
        return new ParameterHandler.FieldMap<>(valueConverter, ((FieldMap) annotation).encoded());

      } else if (annotation instanceof Part) {
        if (!isMultipart) {
          throw parameterError(p, "@Part parameters can only be used with multipart encoding.");
        }
        Part part = (Part) annotation;
        gotPart = true;

        String partName = part.value();
        Class<?> rawParameterType = Utils.getRawType(type);
        if (partName.isEmpty()) {
          if (Iterable.class.isAssignableFrom(rawParameterType)) {
            if (!(type instanceof ParameterizedType)) {
              throw parameterError(p, rawParameterType.getSimpleName()
                  + " must include generic type (e.g., "
                  + rawParameterType.getSimpleName()
                  + "<String>)");
            }
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
            if (!MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(iterableType))) {
              throw parameterError(p,
                  "@Part annotation must supply a name or use MultipartBody.Part parameter type.");
            }
            return ParameterHandler.RawPart.INSTANCE.iterable();
          } else if (rawParameterType.isArray()) {
            Class<?> arrayComponentType = rawParameterType.getComponentType();
            if (!MultipartBody.Part.class.isAssignableFrom(arrayComponentType)) {
              throw parameterError(p,
                  "@Part annotation must supply a name or use MultipartBody.Part parameter type.");
            }
            return ParameterHandler.RawPart.INSTANCE.array();
          } else if (MultipartBody.Part.class.isAssignableFrom(rawParameterType)) {
            return ParameterHandler.RawPart.INSTANCE;
          } else {
            throw parameterError(p,
                "@Part annotation must supply a name or use MultipartBody.Part parameter type.");
          }
        } else {
          Headers headers =
              Headers.of("Content-Disposition", "form-data; name=\"" + partName + "\"",
                  "Content-Transfer-Encoding", part.encoding());

          if (Iterable.class.isAssignableFrom(rawParameterType)) {
            if (!(type instanceof ParameterizedType)) {
              throw parameterError(p, rawParameterType.getSimpleName()
                  + " must include generic type (e.g., "
                  + rawParameterType.getSimpleName()
                  + "<String>)");
            }
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
            if (MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(iterableType))) {
              throw parameterError(p, "@Part parameters using the MultipartBody.Part must not "
                  + "include a part name in the annotation.");
            }
            Converter<?, RequestBody> converter =
                retrofit.requestBodyConverter(iterableType, annotations, methodAnnotations);
            return new ParameterHandler.Part<>(headers, converter).iterable();
          } else if (rawParameterType.isArray()) {
            Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
            if (MultipartBody.Part.class.isAssignableFrom(arrayComponentType)) {
              throw parameterError(p, "@Part parameters using the MultipartBody.Part must not "
                  + "include a part name in the annotation.");
            }
            Converter<?, RequestBody> converter =
                retrofit.requestBodyConverter(arrayComponentType, annotations, methodAnnotations);
            return new ParameterHandler.Part<>(headers, converter).array();
          } else if (MultipartBody.Part.class.isAssignableFrom(rawParameterType)) {
            throw parameterError(p, "@Part parameters using the MultipartBody.Part must not "
                + "include a part name in the annotation.");
          } else {
            Converter<?, RequestBody> converter =
                retrofit.requestBodyConverter(type, annotations, methodAnnotations);
            return new ParameterHandler.Part<>(headers, converter);
          }
        }

      } else if (annotation instanceof PartMap) {
        if (!isMultipart) {
          throw parameterError(p, "@PartMap parameters can only be used with multipart encoding.");
        }
        gotPart = true;
        Class<?> rawParameterType = Utils.getRawType(type);
        if (!Map.class.isAssignableFrom(rawParameterType)) {
          throw parameterError(p, "@PartMap parameter type must be Map.");
        }
        Type mapType = Utils.getSupertype(type, rawParameterType, Map.class);
        if (!(mapType instanceof ParameterizedType)) {
          throw parameterError(p, "Map must include generic types (e.g., Map<String, String>)");
        }
        ParameterizedType parameterizedType = (ParameterizedType) mapType;

        Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
        if (String.class != keyType) {
          throw parameterError(p, "@PartMap keys must be of type String: " + keyType);
        }

        Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
        if (MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(valueType))) {
          throw parameterError(p, "@PartMap values cannot be MultipartBody.Part. "
              + "Use @Part List<Part> or a different value type instead.");
        }

        Converter<?, RequestBody> valueConverter =
            retrofit.requestBodyConverter(valueType, annotations, methodAnnotations);

        PartMap partMap = (PartMap) annotation;
        return new ParameterHandler.PartMap<>(valueConverter, partMap.encoding());

      } else if (annotation instanceof Body) {
        if (isFormEncoded || isMultipart) {
          throw parameterError(p,
              "@Body parameters cannot be used with form or multi-part encoding.");
        }
        if (gotBody) {
          throw parameterError(p, "Multiple @Body method annotations found.");
        }

        Converter<?, RequestBody> converter;
        try {
          converter = retrofit.requestBodyConverter(type, annotations, methodAnnotations);
        } catch (RuntimeException e) {
          // Wide exception range because factories are user code.
          throw parameterError(e, p, "Unable to create @Body converter for %s", type);
        }
        gotBody = true;
        return new ParameterHandler.Body<>(converter);
      }

      return null; // Not a Retrofit annotation.
    }

複製程式碼

這麼超長的程式碼,將我們方法引數的註解,引數型別,引數值都拿到,封裝為 ParameterHandler 並儲存到ParameterHandler<?>[] parameterHandlers集合。

至此,我們將我們建立的service介面所呼叫方法中的請求方式,請求路徑,返回值,請求引數等都獲取到了,並儲存到一個ServiceMethod物件中。

接下來我們再回到 retrofit.Create()

  public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
          ...
          //這裡最終返回到Retrofit中的ServiceMethod<Object, Object>,來儲存我們構建的serviceMethod
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            //再看OkHttpCall
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }
複製程式碼

5. OkHttpCall<Object>

將我們上面構建的serviceMethod 與引數傳進來

  OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) {
    this.serviceMethod = serviceMethod;
    this.args = args;
  }
複製程式碼

最後通過serviceMethod.callAdapter.adapt(okHttpCall)就建立好了我們service的一個實現類。

再看看**callAdapter.adapt(okHttpCall)**這裡的callAdapter是們自定義的callAdapter物件,Android預設的是Call<>;若設定了RxJavaCallAdapterFactory,返回的則是Observable<>,這裡我們看看後一種情況下的實現方式。

RxJavaCallAdapterFactory-->RxJava2CallAdapter 這裡我們看RxJava2CallAdapter的adapt()方法。

 @Override public Object adapt(Call<R> call) {
    Observable<Response<R>> responseObservable = isAsync
        ? new CallEnqueueObservable<>(call)
        : new CallExecuteObservable<>(call);

    Observable<?> observable;
    if (isResult) {
      observable = new ResultObservable<>(responseObservable);
    } else if (isBody) {
      observable = new BodyObservable<>(responseObservable);
    } else {
      observable = responseObservable;
    }

    if (scheduler != null) {
      observable = observable.subscribeOn(scheduler);
    }

    if (isFlowable) {
      return observable.toFlowable(BackpressureStrategy.LATEST);
    }
    if (isSingle) {
      return observable.singleOrError();
    }
    if (isMaybe) {
      return observable.singleElement();
    }
    if (isCompletable) {
      return observable.ignoreElements();
    }
    return observable;
  }
複製程式碼

我們看到但isAsync是非同步情況下時,new CallEnqueueObservable<>(call),建立了一個CallEnqueueObservable 物件,並將retrofit的okhttpCall繼續傳進來.

我們知道在RxJava,Observable的subscribe()觸發了程式碼執行的操作。來看:

@SchedulerSupport(SchedulerSupport.NONE)
    @Override
    public final void subscribe(Observer<? super T> observer) {
            ...
  @Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null");

//這裡建立一個okhttp3.Call的call物件
    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 {
        //這裡看到通過createRawCall() 建立返回了一個call
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }

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

    if (canceled) {
      call.cancel();
    }
//實際上就是通過okhttp3.call來請求
    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);
      }

      @Override public void onFailure(okhttp3.Call call, IOException e) {
          callback.onFailure(OkHttpCall.this, e);
      }

      private void callFailure(Throwable e) {
          callback.onFailure(OkHttpCall.this, e);
      }

      private void callSuccess(Response<T> response) {
          callback.onResponse(OkHttpCall.this, response);
      ...
      }
  }
複製程式碼

createRawCall()

private okhttp3.Call createRawCall() throws IOException {
[1] 建立一個request 請求物件
    Request request = serviceMethod.toRequest(args);
    [2]呼叫newCall(request)返回一個okhttp3.Call 
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

複製程式碼

serviceMethod.toRequest(args)

 Request toRequest(@Nullable Object... args) throws IOException {
 
 //這裡new了一個requestBuilder 將我們獲取到的請求的相關資訊傳進去
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
        contentType, hasBody, isFormEncoded, isMultipart);

    //引數值解析
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
        ...
    for (int p = 0; p < argumentCount; p++) {
      handlers[p].apply(requestBuilder, args[p]);
    }

//這裡呼叫build() 
    return requestBuilder.build();
  }
複製程式碼

下面看一下requestBuilder.build() 是如何建立一個request物件的

 Request build() {
    HttpUrl url;
    HttpUrl.Builder urlBuilder = this.urlBuilder;
    if (urlBuilder != null) {
      url = urlBuilder.build();
    } else {
      // No query parameters triggered builder creation, just combine the relative URL and base URL.
      //noinspection ConstantConditions Non-null if urlBuilder is null.
      url = baseUrl.resolve(relativeUrl);
      if (url == null) {
        throw new IllegalArgumentException(
            "Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl);
      }
    }

    RequestBody body = this.body;
    if (body == null) {
      // Try to pull from one of the builders.
      if (formBuilder != null) {
        body = formBuilder.build();
      } else if (multipartBuilder != null) {
        body = multipartBuilder.build();
      } else if (hasBody) {
        // Body is absent, make an empty body.
        body = RequestBody.create(null, new byte[0]);
      }
    }

    MediaType contentType = this.contentType;
    if (contentType != null) {
      if (body != null) {
        body = new ContentTypeOverridingRequestBody(body, contentType);
      } else {
        requestBuilder.addHeader("Content-Type", contentType.toString());
      }
    }

    return requestBuilder
        .url(url)
        .method(method, body)
        .build();
  }

複製程式碼

接著看看call.enqueue()

@Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

複製程式碼

client.dispatcher().enqueue(new AsyncCall(responseCallback));

  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    
      runningAsyncCalls.add(call);
      //可以看到這裡執行了網路請求,並通過call完成返回請求結果的回撥。
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }
複製程式碼

從上面可以看到,這裡最終是通過建立一個okHttp3.Call物件,構建出一個網路請求的Request,傳送網路請求,並呼叫了call.enqueue()最後將請求到的結果,返回到Retrofit的Callback物件。

package retrofit2;
public interface Callback<T> {
  void onResponse(Call<T> call, Response<T> response);
  void onFailure(Call<T> call, Throwable t);
複製程式碼

到這裡一個請求就結束了。

總結

梳理一下整個過程:

  1. 書寫我們的service請求介面,配置好請求路徑,請求方式,引數配置等。
  2. 構建我們自定義的convert ,convertAdapter 。
  3. 構建retrofit物件,並新增我們的convertFactory,converAdapterFactory等。
  4. 通過retrofit的create()動態代理實現service介面的實現類
  5. 解析 網路請求介面的註解 配置 網路請求引數
  6. 通過 網路請求介面卡 將 網路請求物件 進行平臺適配
  7. 通過 網路請求執行器 傳送網路請求
  8. 通過 資料轉換器 解析伺服器返回的資料
  9. 通過 回撥執行器 切換執行緒(子執行緒 ->>主執行緒)
  10. 最後使用者在主執行緒處理返回結果

參考文章

(參考使用了下面文章的部分內容與資源)

相關文章