Retrofit 源碼分析

簡介

retrofit是square出品的一個優秀的網絡框架,注意,不是一個網絡引擎。它的定位和Volley是同樣的。java

它完成了封裝請求,線程切換,數據裝換等一系列工做,若是本身有能力也能夠封裝一個這種框架,本質上是沒有區別的。android

retrofit使用的網絡引擎是OkHttp.git

而OKHttp和HTTPClient,HttpUrlConnection是一個級別的。github

使用

//1 建立網絡請求接口類
public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

//2 建立Retrofit實例對象
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
     .addConverterFactory(GsonConverterFactory.create())
     .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .build();

//3 經過動態代理建立網絡接口代理對象
GitHubService service = retrofit.create(GitHubService.class);

//4 獲取Call對象
Call<List<Repo>> repos = service.listRepos("octocat");

//5    執行同步請求或異步請求
repos.execute();
repos.enqueue(callback)

Retrofit

Retrofit也是使用Build模式建立的。api

屏幕快照 2019-01-11 下午3.12.35

builder類有這些方法。從圖表能夠看出,咱們能夠調用client方法傳入一個咱們自定義的OkhttpClient,數組

調用baseUrl方法傳入Host,最後調動build方法生成一個Retrofit 對象緩存

public Retrofit build() {
    //baseUrl是必須的
  if (baseUrl == null) {
    throw new IllegalStateException("Base URL required.");
  }

   //若是沒有設置callFactory對象,系統自動生成一個OkhttpClient對象.由於OKHttpclient實現了            Call.Factory接口
   // public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory
  okhttp3.Call.Factory callFactory = this.callFactory;
  if (callFactory == null) {
    callFactory = new OkHttpClient();
  }

  //若是沒有設置callbackExecutor,系統自動生成一個,platform.defaultCallbackExecutor,這個platform是無參構造方法裏調用Platform.get()方法獲得的。
    /** 
    public Builder() {
      this(Platform.get());
    }**/
    
  Executor callbackExecutor = this.callbackExecutor;
  if (callbackExecutor == null) {
    callbackExecutor = platform.defaultCallbackExecutor();
  }

  // Make a defensive copy of the adapters and add the default Call adapter.
  List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
  adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

  // Make a defensive copy of the converters.
  List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

  return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
      callbackExecutor, validateEagerly);
}

Platform

class Platform {
  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 {
        //怎麼還有IOS代碼呢?
      Class.forName("org.robovm.apple.foundation.NSObject");
      return new IOS();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }

  Executor defaultCallbackExecutor() {
    return null;
  }

  CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
    if (callbackExecutor != null) {
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }
    return DefaultCallAdapterFactory.INSTANCE;
  }

  boolean isDefaultMethod(Method method) {
    return false;
  }

  Object invokeDefaultMethod(Method method, Class<?> declaringClass, Object object, Object... args)
      throws Throwable {
    throw new UnsupportedOperationException();
  }

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

    @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
      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);
      }
    }
  }
}

Retrofit 要求必須將請求API寫到一個interface接口文件裏,這是動態代理特性要求的。服務器

從接口文件裏咱們能夠看到,咱們將每一個請求用這種形式表達網絡

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

從接口文件咱們能夠看出,一個請求接口被各類註解所表示。app

咱們知道一個方法有一下關鍵字段組成

首先一個方法必須有描述符,返回值,方法名,參數類型,參數構成。

那咱們用一個方法表示一個http請求須要哪些東西呢?

Http請求,首先咱們得知道是GET請求仍是POST請求,

而後就是請求頭信息,請求路徑,查詢參數等等。

POST請求還須要Body。

Retrofit 已經提供了足夠的註解來表示一個方法。

Retrofit的核心思想AOP,面向切面變成,經過動態代理的反射,將接口文件裏的每一個方法記性處理,也就是分析該方法的註解生成一個ServiceMethod類。

Retrofit 裏有個關鍵的類,ServiceMethod

@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
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, 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對象
          ServiceMethod serviceMethod = loadServiceMethod(method);
          OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
          return serviceMethod.callAdapter.adapt(okHttpCall);
        }
      });
}

從第3步咱們能夠看出create方法的實現就是使用了動態代理,在運行時生成了GitHubService對象。

//建立ServiceMethod對象
ServiceMethod serviceMethod = loadServiceMethod(method);

ServiceMethod loadServiceMethod(Method method) {
  ServiceMethod result;
  synchronized (serviceMethodCache) {
  //先從換從中取改方法對應的ServiceMethod對象,若是爲null就構建一個ServiceMethod對象並存入到map中,若是不爲null直接返回
    result = serviceMethodCache.get(method);
    if (result == null) {
      result = new ServiceMethod.Builder(this, method).build();
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

咱們能夠看到loadServiceMethod(Method method)方法返回了一個ServiceMethod對象
這個serviceMethodCache對象是Retrofit的一個字段,是一個Map集合。

private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();

將接口文件裏每一個方法轉換爲一個ServiceMethod對象後放入改map中做爲緩存,下次調用該方法後就不用再次解析改方法對象了,直接從改map裏去以方法爲key去取對應的ServiceMethod就好了。666

接下來看一下ServiceMethod對象的構造

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;    請求頭部信息
  private final MediaType contentType; 請求參數類型
  private final boolean hasBody;  是否有請求體
  private final boolean isFormEncoded; 是不是格式化的表單
  private final boolean isMultipart; 是否是分塊
  private final ParameterHandler<?>[] parameterHandlers;

  ServiceMethod(Builder<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;
  }
}

ServiceMethod是採用Builder模式建立的。

static final class Builder<T> {
  final Retrofit retrofit;
  final Method method;         //接口裏生命的方法
  final Annotation[] methodAnnotations;  //方法的註解,get/post/header之類的
  final Annotation[][] parameterAnnotationsArray; //方法的參數註解數組,二維數組
  final Type[] parameterTypes;  //方法的參數數組

  Type responseType;
  boolean gotField;
  boolean gotPart;
  boolean gotBody;
  boolean gotPath;
  boolean gotQuery;
  boolean gotUrl;
  String httpMethod;
  boolean hasBody;
  boolean isFormEncoded;
  boolean isMultipart;
  String relativeUrl;
  Headers headers;
  MediaType contentType;
  Set<String> relativeUrlParamNames;
  ParameterHandler<?>[] parameterHandlers;
  Converter<ResponseBody, T> responseConverter;
  CallAdapter<?> callAdapter;

  public Builder(Retrofit retrofit, Method method) {
    this.retrofit = retrofit;
    this.method = method;
    this.methodAnnotations = method.getAnnotations(); //獲取方法的註解
    this.parameterTypes = method.getGenericParameterTypes(); //獲取被註解修飾的方法,一個數組
    this.parameterAnnotationsArray = method.getParameterAnnotations(); //獲取方法的參數註解信息,是一個二維數組
  }

Builder的構造參數須要一個Retrofit對象和一個Method對象。

首先解析方法對象,將其註解和參數註解放到對應的數組裏。

首先在構造方法裏獲取該方法的註解,方法的參數,以及每一個參數的註解。

關鍵就在build方法,在build方法裏對方法作了一個完全的分解

public ServiceMethod build() {
  //1 處理返回結果,作必定的轉換
  callAdapter = createCallAdapter();
  responseType = callAdapter.responseType();
  if (responseType == Response.class || responseType == okhttp3.Response.class) {
    throw methodError("'"
        + Utils.getRawType(responseType).getName()
        + "' is not a valid response body type. Did you mean ResponseBody?");
  }
  responseConverter = createResponseConverter();

    //2提取方法的註解
  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }
    //若是httpMethod爲null,即沒有使用方法類型註解修飾,拋出異常進行提示
  if (httpMethod == null) {
    throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
  }
//若是沒有請求體,即便用了GET,HEAD,DELETE,OPTIONS等所修飾,即不涉及到表單的提交,可是同時使用了Multipart,或者FormUrlEncoded所修飾,就報錯
  if (!hasBody) {
    if (isMultipart) {
      throw methodError(
          "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
    }
    if (isFormEncoded) {
      throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
          + "request body (e.g., @POST).");
    }
  }

  //3提取方法的參數
  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);
  }

  //相對路徑爲null且gotURL爲false的話,拋出異常,由於沒有相對路徑沒法請求。
  if (relativeUrl == null && !gotUrl) {
    throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
  }
  //沒有使用@FormUrlEncoded,@Multipart主機而且hasBody爲false,可是gotBody爲true,拋出異常,提示
    Non-Body類型的HTTP method 不能參數不能使用@Body註解
  if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
    throw methodError("Non-body HTTP method cannot contain @Body.");
  }
  //使用@FormUrlEncoded修飾的方法中的參數至少有一個參數被@Field註解修飾
  if (isFormEncoded && !gotField) {
    throw methodError("Form-encoded method must contain at least one @Field.");
  }
    
  //使用@Multipart修飾的方法中的參數至少有一個參數被@Part註解修飾
  if (isMultipart && !gotPart) {
    throw methodError("Multipart method must contain at least one @Part.");
  }

 //4 當前Builder對象初始化完畢,能夠用來夠着ServiceMethod對象。
  return new ServiceMethod<>(this);
}

處理返回結果

private CallAdapter<?> createCallAdapter() {
  //獲取方法的返回結果,若是有不能解析的類型則拋出異常,也就是說接口中定義的方法的返回值不能使用泛型
  Type returnType = method.getGenericReturnType();
  if (Utils.hasUnresolvableType(returnType)) {
    throw methodError(
        "Method return type must not include a type variable or wildcard: %s", returnType);
  }
   //接口裏的方法不能返回void
  if (returnType == void.class) {
    throw methodError("Service methods cannot return void.");
  }
  Annotation[] annotations = method.getAnnotations();
  try {
    return retrofit.callAdapter(returnType, annotations);
  } catch (RuntimeException e) { // Wide exception range because factories are user code.
    //用戶自定義的Adapter可能不能正確的處理返回結果,這時候拋出異常
    throw methodError(e, "Unable to create call adapter for %s", returnType);
  }
}

解析方法註解

1到處理方法的註解,就是先處理GET/POST/Header等註解信息

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) {
  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) {//若是是Multipart註解
    if (isFormEncoded) {
    //若是同時使用了FormUrlEncoded註解報錯
      throw methodError("Only one encoding annotation is allowed.");
    }
    isMultipart = true;
  } else if (annotation instanceof FormUrlEncoded) {
    if (isMultipart) {
    //若是同時使用了Multipart註解報錯,從這咱們能夠看出一個方法不能同時被Multipart和FormUrlEncoded所修飾
      throw methodError("Only one encoding annotation is allowed.");
    }
    isFormEncoded = true;
  }
}

而後根據具體的註解類型,在作進一步的處理,這裏主要分析GET/POST/HEADER/ 等註解

@GET

else if (annotation instanceof GET) {
  parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
}

get類型的請求,沒有請求體

private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
 //若是該Builder已經有HTTPMethod了就不能改變了,直接拋異常
    if (this.httpMethod != null) {
    throw methodError("Only one HTTP method is allowed. Found: %s and %s.",
        this.httpMethod, httpMethod);
  }
  //將HTTPMethod賦值給httpMethod對象,Get、Post、Delete等
  this.httpMethod = httpMethod;
  this.hasBody = hasBody;//是否有請求體

    //若是value爲null,返回,由於value參數的值其實就是relativeURL。因此不能爲null
  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()) {
        //若是在value裏面找到裏查詢參數的話,拋出異常。由於查詢參數可使用@Query註解來動態配置。
      throw methodError("URL query string \"%s\" must not have replace block. "
          + "For dynamic query parameters use @Query.", queryParams);
    }
  }

  this.relativeUrl = value; //將value賦值給relativeUrl
  this.relativeUrlParamNames = parsePathParameters(value); //獲取value裏面的path佔位符,若是有的話
}

再來看下解析value裏的path佔位符的方法。

/**
獲取已知URI裏面的路徑集合,若是一個參數被使用了兩次,它只會在set中出現一次,好拗口啊,使用LinkedHashSet來保存path參數集合,保證了路徑參數的順序。
 * Gets the set of unique path parameters used in the given URI. If a parameter is used twice
 * in the URI, it will only show up once in the set.
 */
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;
}

至此,GET方法的相關的註解分析完畢

@POST

else if (annotation instanceof POST) {
  parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
}

POST類型的請求,沒有請求體。因此hasBody參數爲true。

parseHttpMethodAndPath()方法已將在GET方法裏面分析過了,這裏面都同樣。

其餘的請求類型也是大同小異。

而後接着分析方法的Header註解

@Headers

else if (annotation instanceof retrofit2.http.Headers) {
 //   首先獲取Headers註解的值,是一個字符串數組。
  String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
    若是header註解長度爲0,拋出異常,因此使用了header註解必須設置值,不能存在空的header
  if (headersToParse.length == 0) {
    throw methodError("@Headers annotation is empty.");
  }
    處理header信息,我猜確定是一個map
  headers = parseHeaders(headersToParse);

啊,竟然不是,666.由於header不是KV結構的數據類型,而是一個key能夠對應多個值。理論上可使用Map<String,Set<String>>表示。

private Headers parseHeaders(String[] headers) {
  Headers.Builder builder = new Headers.Builder();
  for (String header : headers) {
  // header以「:"分割,前面是key,後面是value
    int colon = header.indexOf(':');
    if (colon == -1 || colon == 0 || colon == header.length() - 1) {
    //header必須是key:value格式表示,否則報錯
      throw methodError(
          "@Headers value must be in the form \"Name: Value\". Found: \"%s\"", header);
    }
    String headerName = header.substring(0, colon); //key值
    String headerValue = header.substring(colon + 1).trim(); //value值,必須是一個數組,艹,又猜錯了。
    if ("Content-Type".equalsIgnoreCase(headerName)) {
    //遇到"Content-Type"字段。還須要得到具體的MediaType。
      MediaType type = MediaType.parse(headerValue);
      if (type == null) {
      //若是mediaType爲null。拋出一個type畸形的錯誤。
        throw methodError("Malformed content type: %s", headerValue);
      }
      contentType = type;
    } else {
    將header的key和value加入到Builder裏面。
      builder.add(headerName, headerValue);
    }
  }
  最後調用build方法生成一個Header對愛。
  return builder.build();
}
/**
 * Add a header with the specified name and value. Does validation of header names and values.
 */
public Builder add(String name, String value) {
  checkNameAndValue(name, value);
  return addLenient(name, value);
}
Builder addLenient(String name, String value) {
  namesAndValues.add(name);
  namesAndValues.add(value.trim());
  return this;
}
final List<String> namesAndValues = new ArrayList<>(20);

namesAndValues是Header.Builder類的一種子段。可見在Builder內部header信息是按照key/value異常放到一個String集合裏面的。爲何不放到一個Map裏面呢,不懂。

總之,最後就是講方法的Headers註解信息提取完畢。

處理方法參數

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);
  }
//獲取第p個參數的註解數組,若是沒有註解拋出異常,可見,使用了Retrofit,接口方法中每一個參數都必須使用註解進行修飾。
  Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
  if (parameterAnnotations == null) {
    throw parameterError(p, "No Retrofit annotation found.");
  }

   //解析方法中的參數,存入parameterHandlers[]數組中。
  parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}

參數校驗

Utils.hasUnresolvableType(parameterType),這個方法是對參數的類型作個校驗。

static boolean hasUnresolvableType(Type type) {
  //若是參數是引用數據類型,返回false,可見,接口定義中方法的參數只能是基本數據類型
  if (type instanceof Class<?>) {
    return false;
  }
  //若是參數是泛型
  if (type instanceof ParameterizedType) {
    ParameterizedType parameterizedType = (ParameterizedType) type;
    //去除泛型類中的實際類型,遍歷
    for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
    //若是有一個泛型參數是基本數據類型,返回true,都不是返回false
      if (hasUnresolvableType(typeArgument)) {
        return true;
      }
    }
    return false;
  }
  //若是參數是泛型數組類型
  if (type instanceof GenericArrayType) {
    return hasUnresolvableType(((GenericArrayType) type).getGenericComponentType());
  }
  if (type instanceof TypeVariable) {
    return true;
  }
  if (type instanceof WildcardType) {
    return true;
  }
  String className = type == null ? "null" : type.getClass().getName();
  throw new IllegalArgumentException("Expected a Class, ParameterizedType, or "
      + "GenericArrayType, but <" + type + "> is of type " + className);
}

解析參數

parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
private ParameterHandler<?> parseParameter(
        int p, Type parameterType, Annotation[] annotations) {
      ParameterHandler<?> result = null;
    //遍歷參數的註解數組,調用parseParameterAnnotation()
      for (Annotation annotation : annotations) {
        ParameterHandler<?> annotationAction = parseParameterAnnotation(
            p, parameterType, annotations, annotation);
        //若是該註解沒有返回,則解析下一個註解
        if (annotationAction == null) {
          continue;
        }

        if (result != null) {
          throw parameterError(p, "Multiple Retrofit annotations found, only one allowed.");
        }
        
        result = annotationAction; //將解析的結果賦值給Result
      }

    //若是註解爲null,拋出異常。這個地方永遠不會調用,由於在獲取註解數組以前就作過判斷了,若是註解數組爲null,直接拋異常,Line197-Line200 in ServiceMethod.Builder中
      if (result == null) {
        throw parameterError(p, "No Retrofit annotation found.");
      }

      return result;
    }

獲取參數註解信息

再來看看parseParameterAnnotation()方法,內容略多

private ParameterHandler<?> parseParameterAnnotation(
    int p, Type type, Annotation[] annotations, Annotation annotation) {
  if (annotation instanceof Url) {
      //若是使用了Url註解,
    if (gotUrl) {
        //若是gotUrl爲true,由於gotURL默認爲false,說明以前處理過Url註解了,拋出多個@Url註解異常
      throw parameterError(p, "Multiple @Url method annotations found.");
    }
    if (gotPath) {
        //若是gotPath爲true,拋出異常,說明@Path註解不能和@Url註解一塊兒使用
      throw parameterError(p, "@Path parameters may not be used with @Url.");
    }
    if (gotQuery) {
        //若是gotQuery爲true,拋出異常,說明@Url註解不能用在@Query註解後面
      throw parameterError(p, "A @Url parameter must not come after a @Query");
    }
    if (relativeUrl != null) {
        //若是relativeUrl不爲null,拋出異常,說明使用了@Url註解,relativeUrl必須爲null
      throw parameterError(p, "@Url cannot be used with @%s URL", httpMethod);
    }

    gotUrl = true;
      
----------------------------------------------------------------------------------------    
      //若是參數類型是HttpURL,String,URI或者參數類型是「android.net.Uri",返回ParameterHandler.RelativeUrl(),實際是交由這個類處理
    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 {
        //否則就拋出異常,也就是說@Url註解必須使用在okhttp3.HttpUrl, String, java.net.URI, or android.net.Uri 這幾種類型的參數上。
      throw parameterError(p,
          "@Url must be okhttp3.HttpUrl, String, java.net.URI, or android.net.Uri type.");
    }
------------------------------------------------------------------------------------------
  } else if (annotation instanceof Path) { //@Path註解
     //若是gotQuery爲true。拋出異常,由於@Path修飾的參數是路徑的佔位符。不是查詢參數,不能使用@Query註解修飾
    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.");
    }
      //若是相對路徑爲null,那@path註解也就無心義了。
    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(); //獲取@Path註解的值
    validatePathName(p, name); //對改值進行校驗,1該value必須是合法字符,2:該相對路徑必須包含相應的佔位符
 
      //而後將改參數的全部註解進行處理,最終調用ParameterHandler.Path進行處理。
    Converter<?, String> converter = retrofit.stringConverter(type, annotations);
    return new ParameterHandler.Path<>(name, converter, path.encoded());

  } else if (annotation instanceof Query) { //Query註解,看不太懂,最後也是調用ParameterHandler.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 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裏對應的子類來處理

寫到這裏我已經暈了。暈暈乎乎好舒服

@Header

使用場景

有時候咱們須要動態的設置請求header中的某個請求頭的值,這個時候就可使用@Header來修飾個參數。

最終都是講header裏的信息提取到Request裏面

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

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

  @Override void apply(RequestBuilder builder, T value) throws IOException {
    if (value == null) return; // Skip null values.
    builder.addHeader(name, valueConverter.convert(value));
  }
}
void addHeader(String name, String value) {
  if ("Content-Type".equalsIgnoreCase(name)) {
    MediaType type = MediaType.parse(value);
    if (type == null) {
      throw new IllegalArgumentException("Malformed content type: " + value);
    }
    contentType = type;
  } else {
    requestBuilder.addHeader(name, value);
  }
}

調用requestBuilder.addHeader()方法。

這個requestBuilder是OKHttp中Request的內部靜態類Builder類的一個對象。

private final Request.Builder requestBuilder;

從中咱們能夠看出最後將@Header註釋的參數的值解析後添加到Request對象中的Header信息裏。

@Path

使用場景

有時候請求路徑是不定的,即請求路徑裏的某個segment是變化的,也就是須要咱們使用參數來動態的改變,這個時候咱們就須要使用@Path 來修飾這個參數

static final class Path<T> extends ParameterHandler<T> {
  private final String name; //參數名,佔位符
  private final Converter<T, String> valueConverter;
  private final boolean encoded; //是否編碼

  Path(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) {
      throw new IllegalArgumentException(
          "Path parameter \"" + name + "\" value must not be null.");
    }
    builder.addPathParam(name, valueConverter.convert(value), encoded);
  }
}
void addPathParam(String name, String value, boolean encoded) {
  if (relativeUrl == null) {
    // The relative URL is cleared when the first query parameter is set.
    throw new AssertionError();
  }
   //將佔位符」{name}」使用value替換
  relativeUrl = relativeUrl.replace("{" + name + "}", canonicalizeForPath(value, encoded));
}

@Query

使用場景

@Query用來修飾接口方法中的查詢字段

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

  Query(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.addQueryParam(name, valueConverter.convert(value), encoded);
  }
}
//將查詢參數組合到相對路徑上。
void addQueryParam(String name, String value, boolean encoded) {
  if (relativeUrl != null) {
    // Do a one-time combination of the built relative URL and the base URL.
    urlBuilder = baseUrl.newBuilder(relativeUrl);
    if (urlBuilder == null) {
      throw new IllegalArgumentException(
          "Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl);
    }
    relativeUrl = null;
  }

  if (encoded) {
    urlBuilder.addEncodedQueryParameter(name, value);
  } else {
    urlBuilder.addQueryParameter(name, value);
  }
}

@QueryMap

使用場景

當接口中的一個 方法有比較多的查詢字段時,所有定義到方法中時比較麻煩且容易出錯,這個使用咱們徹底能夠將全部的查詢參數放到一個Map裏面。

可想而知,其內部實現一定是遍歷map ,而後像處理@Query參數同樣調用addQueryParam()處理每一個查詢參數。

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

  FieldMap(Converter<T, String> valueConverter, boolean encoded) {
    this.valueConverter = valueConverter;
    this.encoded = encoded;
  }

  @Override void apply(RequestBuilder builder, Map<String, T> value) throws IOException {
    if (value == null) {
      throw new IllegalArgumentException("Field map was null.");
    }

    for (Map.Entry<String, T> entry : value.entrySet()) {
      String entryKey = entry.getKey();
      if (entryKey == null) {
        throw new IllegalArgumentException("Field map contained null key.");
      }
      T entryValue = entry.getValue();
      if (entryValue == null) {
        throw new IllegalArgumentException(
            "Field map contained null value for key '" + entryKey + "'.");
      }
      //果真不假
      builder.addFormField(entryKey, valueConverter.convert(entryValue), encoded);
    }
  }
}

@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. 因此使用@Field修飾的字段,是不會上傳到服務器的。
    //調用ResuestBuilder對象的具體想法來處理@Field修飾的表單字段
    builder.addFormField(name, valueConverter.convert(value), encoded);
  }
}
void addFormField(String name, String value, boolean encoded) {
//根據參數值是否被編碼,調用不一樣的方法。formBuilder是OKHttp中的一個類。也是使用Builder模式建立的。
  if (encoded) {
    formBuilder.addEncoded(name, value);
  } else {
    formBuilder.add(name, value);
  }
}

@FieldMap

@FieldMap

使用場景

假如表單參數有不少個,咱們可使用一個Map<String,String>來表示,而後使用@FieldMap註解來修飾該參數就好了。可想而知,如同@QueryMap同樣,其內部實現確定是遍歷Map,而後像處理@Field參數同樣調用

builder.addFormField(name, valueConverter.convert(value), encoded);

@Body

使用場景

在如下須要提交表單的請求裏,咱們可使用@Field,@FieldMap,咱們還可使用@Body來修飾咱們提交的表單數據,這個時候咱們須要定義一個Bean類,Bean類的各個Field必須和表單字段的key同樣

static final class Body<T> extends ParameterHandler<T> {
  private final Converter<T, RequestBody> converter;

  Body(Converter<T, RequestBody> converter) {
    this.converter = converter;
  }

  @Override void apply(RequestBuilder builder, T value) {
    if (value == null) {
      throw new IllegalArgumentException("Body parameter value must not be null.");
    }
    RequestBody body;
    try {
      body = converter.convert(value);
    } catch (IOException e) {
      throw new RuntimeException("Unable to convert " + value + " to RequestBody", e);
    }
    builder.setBody(body);
  }
}

這裏Retrofit並無像@Field同樣處理表單參數。仔細想一想也對,由於凡是提交的表單數據都須要放到請求體裏面,即便使用@Field,@FieldMap提交的數據,最終仍是須要放到請求體裏面。

@Part

@RawPart

@PartMap

以上三個註解都是使用修飾上傳文件的參數的,

結論

從對上面的分析能夠知道,咱們在提取使用註解修飾的參數後將值存放到RequestBuilder對象裏。

這裏又引入了RequestBuilder類

RequestBuilder

final class RequestBuilder {
  private static final char[] HEX_DIGITS =
      { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
  private static final String PATH_SEGMENT_ALWAYS_ENCODE_SET = " \"<>^`{}|\\?#";

  private final String method; //方法類型

  private final HttpUrl baseUrl; //scheme+host
  private String relativeUrl;     //相對路徑
  private HttpUrl.Builder urlBuilder; //URL構造器

  private final Request.Builder requestBuilder; //OkHttp中Request構造器
  private MediaType contentType;        //提交表單的數據類型

  private final boolean hasBody;        //是否有請求體
  private MultipartBody.Builder multipartBuilder; //上傳文件的構造器
  private FormBody.Builder formBuilder;                //表單數據的構造器
  private RequestBody body;                            //請求體

  RequestBuilder(String method, HttpUrl baseUrl, String relativeUrl, Headers headers,
      MediaType contentType, boolean hasBody, boolean isFormEncoded, boolean isMultipart) {
    this.method = method;
    this.baseUrl = baseUrl;
    this.relativeUrl = relativeUrl;
    this.requestBuilder = new Request.Builder();
    this.contentType = contentType;
    this.hasBody = hasBody;

    if (headers != null) {
      requestBuilder.headers(headers);
    }

    if (isFormEncoded) {
      // Will be set to 'body' in 'build'.
      formBuilder = new FormBody.Builder();
    } else if (isMultipart) {
      // Will be set to 'body' in 'build'.
      multipartBuilder = new MultipartBody.Builder();
      multipartBuilder.setType(MultipartBody.FORM);
    }
  }

  void setRelativeUrl(Object relativeUrl) {
    if (relativeUrl == null) throw new NullPointerException("@Url parameter is null.");
    this.relativeUrl = relativeUrl.toString();
  }

  void addHeader(String name, String value) {
    if ("Content-Type".equalsIgnoreCase(name)) {
      MediaType type = MediaType.parse(value);
      if (type == null) {
        throw new IllegalArgumentException("Malformed content type: " + value);
      }
      contentType = type;
    } else {
      requestBuilder.addHeader(name, value);
    }
  }

  void addPathParam(String name, String value, boolean encoded) {
    if (relativeUrl == null) {
      // The relative URL is cleared when the first query parameter is set.
      throw new AssertionError();
    }
    relativeUrl = relativeUrl.replace("{" + name + "}", canonicalizeForPath(value, encoded));
  }

  private static String canonicalizeForPath(String input, boolean alreadyEncoded) {
    int codePoint;
    for (int i = 0, limit = input.length(); i < limit; i += Character.charCount(codePoint)) {
      codePoint = input.codePointAt(i);
      if (codePoint < 0x20 || codePoint >= 0x7f
          || PATH_SEGMENT_ALWAYS_ENCODE_SET.indexOf(codePoint) != -1
          || (!alreadyEncoded && (codePoint == '/' || codePoint == '%'))) {
        // Slow path: the character at i requires encoding!
        Buffer out = new Buffer();
        out.writeUtf8(input, 0, i);
        canonicalizeForPath(out, input, i, limit, alreadyEncoded);
        return out.readUtf8();
      }
    }

    // Fast path: no characters required encoding.
    return input;
  }

  private static void canonicalizeForPath(Buffer out, String input, int pos, int limit,
      boolean alreadyEncoded) {
    Buffer utf8Buffer = null; // Lazily allocated.
    int codePoint;
    for (int i = pos; i < limit; i += Character.charCount(codePoint)) {
      codePoint = input.codePointAt(i);
      if (alreadyEncoded
          && (codePoint == '\t' || codePoint == '\n' || codePoint == '\f' || codePoint == '\r')) {
        // Skip this character.
      } else if (codePoint < 0x20 || codePoint >= 0x7f
          || PATH_SEGMENT_ALWAYS_ENCODE_SET.indexOf(codePoint) != -1
          || (!alreadyEncoded && (codePoint == '/' || codePoint == '%'))) {
        // Percent encode this character.
        if (utf8Buffer == null) {
          utf8Buffer = new Buffer();
        }
        utf8Buffer.writeUtf8CodePoint(codePoint);
        while (!utf8Buffer.exhausted()) {
          int b = utf8Buffer.readByte() & 0xff;
          out.writeByte('%');
          out.writeByte(HEX_DIGITS[(b >> 4) & 0xf]);
          out.writeByte(HEX_DIGITS[b & 0xf]);
        }
      } else {
        // This character doesn't need encoding. Just copy it over.
        out.writeUtf8CodePoint(codePoint);
      }
    }
  }

  void addQueryParam(String name, String value, boolean encoded) {
    if (relativeUrl != null) {
      // Do a one-time combination of the built relative URL and the base URL.
      urlBuilder = baseUrl.newBuilder(relativeUrl);
      if (urlBuilder == null) {
        throw new IllegalArgumentException(
            "Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl);
      }
      relativeUrl = null;
    }

    if (encoded) {
      urlBuilder.addEncodedQueryParameter(name, value);
    } else {
      urlBuilder.addQueryParameter(name, value);
    }
  }

  void addFormField(String name, String value, boolean encoded) {
    if (encoded) {
      formBuilder.addEncoded(name, value);
    } else {
      formBuilder.add(name, value);
    }
  }

  void addPart(Headers headers, RequestBody body) {
    multipartBuilder.addPart(headers, body);
  }

  void addPart(MultipartBody.Part part) {
    multipartBuilder.addPart(part);
  }

  void setBody(RequestBody body) {
    this.body = body;
  }

  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.
      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());
      }
    }

    //生成一個Request對象
    return requestBuilder
        .url(url)
        .method(method, body)
        .build();
  }

  private static class ContentTypeOverridingRequestBody extends RequestBody {
    private final RequestBody delegate;
    private final MediaType contentType;

    ContentTypeOverridingRequestBody(RequestBody delegate, MediaType contentType) {
      this.delegate = delegate;
      this.contentType = contentType;
    }

    @Override public MediaType contentType() {
      return contentType;
    }

    @Override public long contentLength() throws IOException {
      return delegate.contentLength();
    }

    @Override public void writeTo(BufferedSink sink) throws IOException {
      delegate.writeTo(sink);
    }
  }
}

OkHttpCall

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

在建立了ServiceMethod對象後,使用該ServiceMethod對象和其參數建立一個OKHttPCall對象

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

在合適的時候調用ServiceMethod對象的toRequest方法生成一個Request對象,toReques()的內部實現就是調用RequestBuilder對象的build方法。

/** Builds an HTTP request from method arguments. */
Request toRequest(Object... args) throws IOException {
  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 + ")");
  }

  for (int p = 0; p < argumentCount; p++) {
    handlers[p].apply(requestBuilder, args[p]);
  }

  return requestBuilder.build();
}

OkHttpCall 實現了Call接口,這個Call接口和OkHttp中的Call接口同樣,畢竟一家公司嘛。

其實就是對OkHttpCall 作了一層包裝。

最後方法的執行時經過調用

return serviceMethod.callAdapter.adapt(okHttpCall);

返回接口中方法定義的返回值。

這塊的流程就是構造一個OKHttp對象須要使用ServiceMethod對象和相應的參數。

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

最後建立具體的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;
}

調用了ServiceMethod對象的toRequest方法,而後使用這個request對象建立了一個Call對象。

/** Builds an HTTP request from method arguments. */
Request toRequest(Object... args) throws IOException {
  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 + ")");
  }

  for (int p = 0; p < argumentCount; p++) {
    handlers[p].apply(requestBuilder, args[p]);
  }
//生成一個Request對象
  return requestBuilder.build();
}
相關文章
相關標籤/搜索