划水:Retrofit三步走:request篇

​ 在流程梳理篇,我們介紹了ServiceMethod是其retrofit的核心部分,這篇我們就着重分析retrofit的ServiceMethod這個處理類

build建造者部分

對於ServiceMethod這部分的build,其構造如下:

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

這裏傳入了retrofit本身和目標的方法method
 
從method的getAnnotations/getGenericParameterTypes/getParameterAnnotations
這些方法中,可以看出retrofit這部分就是想方設法從method上獲取到所有的信息

從上面的構造入手,一下子就想到在使用的時候我們的一些做法,舉例:

@FormUrlEncoded
    @POST("openapi.do")
    public abstract Call<TranslateBean> hello(@Field("keyfrom") String keyfrom,
                                              @Field("key") String key);

衆所周知,一個request的組成基本上參數部分都是key-vale形式組成,那就不難理解爲什麼retrofit要求我們從通過方法註解/參數註解這些方式傳入對應的關係了

理解了參數部分,我們看看build建造者最關鍵的方法build方法:

build方法較多,我們先摺疊起來,看前半部分:

public ServiceMethod build() {
    callAdapter = createCallAdapter();
    responseType = callAdapter.responseType();
    responseConverter = createResponseConverter();
      ...省略部分代碼
    return new ServiceMethod<>(this);
}

build方法上首先創建了一個callAdapter,提起callAdapter不免立馬想到我們使用時傳入的RxJavaCallAdapterFactory部分,我們順着createCallAdapter看看跟我們RxJavaCallAdapterFactory有沒有關係,

private CallAdapter<T, R> createCallAdapter() {
  		/**
  		 * 首先通過method獲取返回值類型,緊跟着對返回值類型合法性驗證
  		 */
      Type returnType = method.getGenericReturnType();
      if (Utils.hasUnresolvableType(returnType)) {
        throw methodError(
            "Method return type must not include a type variable or wildcard: %s", returnType);
      }
      if (returnType == void.class) {
        throw methodError("Service methods cannot return void.");
      }
  		/**
  		 * 接着獲取方法上的註解,根據返回值類型和方法上的註解,調用retrofit的callAdapter
  		 */
      Annotation[] annotations = method.getAnnotations();
      try {
        //noinspection unchecked
        return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
      } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(e, "Unable to create call adapter for %s", returnType);
      }
    }

而對於retrofit的callAdapter方法其上註解是這樣描述的:

 /**
   * Returns the {@link CallAdapter} for {@code returnType} from the available {@linkplain
   * #callAdapterFactories() factories}.
   *
   * @throws IllegalArgumentException if no call adapter available for {@code type}.
   */
  public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }

即返回這種返回值類型的callDApter從callAdapterFactories中,那就很顯然了,那就跟我們預想的一樣,RxJavaCallAdapterFactory中返回目標返回值類型的callAdapter

對於callAdapter這部分是retrofit的高明之處,通過這個callAdapter大大增加了其語言的擴展性

CallAdapter部分

在callAdapter這裏呢,只有兩個方法,一個是Type responseType();,另一個是T adapt(Call<R> call);

這個方法是返回在請求響應時我們接收的bean的值類型對象
Type responseType();
而最後這個方法是返回一個okhttp執行請求的call代理對象
T adapt(Call<R> call);

順着上面的思路繼續,在build方法中,緊跟着從callAdapter的responseType中獲取返回值類型,根據這個返回值類型,調用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);
      }
    }
這個方法就與callAdapter很相似了,也是通過獲取method方法上的參數,根據返回值類型和註解,調用retrofit的responseBodyConverter

對於responseBodyConverter這部分處理與callAdapter極其的相似

ResponseBodyConverter部分

一提到相似,我們也就能反應過來,我們傳入的GsonConverterFactory,也是根據我們的要求返回我們所要的ResponseBodyConverter

/**
   * Returns a {@link Converter} for {@link ResponseBody} to {@code type} from the available
   * {@linkplain #converterFactories() factories} except {@code skipPast}.
   *
   * @throws IllegalArgumentException if no converter available for {@code type}.
   */
  public <T> Converter<ResponseBody, T> nextResponseBodyConverter(Converter.Factory skipPast,
      Type type, Annotation[] annotations) {
    checkNotNull(type, "type == null");
    checkNotNull(annotations, "annotations == null");

    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
      /**
       * 通過converterFactory找到我們要的converter來進行轉換
       */
      Converter<ResponseBody, ?> converter =
          converterFactories.get(i).responseBodyConverter(type, annotations, this);
      if (converter != null) {
        //noinspection unchecked
        return (Converter<ResponseBody, T>) converter;
      }
    }
		...省略部分代碼...
  }

既然通過createCallAdater和createResponseConverter這兩個方法,把耦合的入口和出口都處理好了,下面就是進入參數處理的階段了:

		/**
		 * 通過method的註解遍歷,對method的註解進行處理
		 */
		for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }

      ...省略部分代碼...

      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        Type parameterType = parameterTypes[p];
        if (Utils.hasUnresolvableType(parameterType)) {
          throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
              parameterType);
        }

        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
        if (parameterAnnotations == null) {
          throw parameterError(p, "No Retrofit annotation found.");
        }

        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }
      ...省略部分代碼...

剩餘的方法主要就是parseMethodAnnotation和parseParameter這兩個方法了

parseMethodAnnotation

在這裏插入圖片描述

這裏只剪輯parseMethodAnnotation不過殊途同歸,最終處理都是在parseHttpMethodAndPath中,對於parseHttpMethodAndPath

parseHttpMethodAndPath

private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
      ...省略部分代碼...
      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);
        ...省略部分代碼...
      }

      this.relativeUrl = value;
      this.relativeUrlParamNames = parsePathParameters(value);
    }

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);

/**
 * 完成對url的處理
 */
static Set<String> parsePathParameters(String path) {
    Matcher m = PARAM_URL_REGEX.matcher(path);
    Set<String> patterns = new LinkedHashSet<>();
    while (m.find()) {
      patterns.add(m.group(1));
    }
    return patterns;
  }
最終我們得到目標方法的請求路徑

由於parseParameterAnnotation情況分類比較多,這裏僅拿field來進行講解說明

...省略部分代碼...
/**
 * 首先獲取字段名稱
 */
Field field = (Field) annotation;
String name = field.value();
boolean encoded = field.encoded();

gotField = true;

/**
 * 首先獲取值類型
 */
Class<?> rawParameterType = Utils.getRawType(type);
/**
 * 如果值類型是Iterable的子類
 */
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;
  /**
   * 如果值類型是Iterable的子類,通過getParameterUpperBound獲取實際類型的類型
   */
   Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
  	/**
     * 通過type和註解找到轉換的converter
     */
   Converter<?, String> converter =
              retrofit.stringConverter(iterableType, annotations);
  	/**
  	 * 將轉換器/字段名稱和字段是否encode處理存放在ParameterHandler的Field中
  	 */
   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);
}

將所有paramterHandler存放在builder的parameterHandlers中

ParameterHandler

ParameterHandlerd的實現不同實現有很多,有Body/RawPart/FieldMap/Field等等,這裏用Field舉例:

 static final class Field<T> extends ParameterHandler<T> {
    private final String name;
    private final Converter<T, String> valueConverter;
    private final boolean encoded;

    Field(String name, Converter<T, String> valueConverter, boolean encoded) {
      this.name = checkNotNull(name, "name == null");
      this.valueConverter = valueConverter;
      this.encoded = encoded;
    }

    @Override void apply(RequestBuilder builder, T value) throws IOException {
      if (value == null) return; // Skip null values.
      builder.addFormField(name, valueConverter.convert(value), encoded);
    }
  }

所有實現最關鍵的apply方法,向RequestBuilder中addFormField操作 構建請求數據

到這裏Builder相關的方法就結束了

toRequest

toRequest方法是ServiceMethod其中的一個方法,從上面我們片面的瞭解parameterHandler幫我們構建form請求數據,這個方法就是ServiceMethod幫助我們統一進行對Request的創建

Request toRequest(Object... args) throws IOException {
  	/**
  	 * 根據目標方法/baseUrl等創建RequestBuilder
  	 */
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
        contentType, hasBody, isFormEncoded, isMultipart);

    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.

    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

    int argumentCount = args != null ? args.length : 0;
    if (argumentCount != handlers.length) {
      throw new IllegalArgumentException("Argument count (" + argumentCount
          + ") doesn't match expected count (" + handlers.length + ")");
    }
		/**
  	 * 將之前構建的所有paramterhandler進行request處理
  	 */
    for (int p = 0; p < argumentCount; p++) {
      handlers[p].apply(requestBuilder, args[p]);
    }

    return requestBuilder.build();
  }

至此retrofit部分的request處理就到這結束了,由於reqeustBuilder內容相對清晰,容易分析,這裏就不加大篇幅來闡述這裏的內容

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章