国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Retrofit 源碼分析

Kahn / 3490人閱讀

摘要:我們知道一個方法有一下關鍵字段組成首先一個方法必須有描述符,返回值,方法名,參數類型,參數構成。至此,方法的相關的注解分析完畢類型的請求,沒有請求體。方法已將在方法里面分析過了,這里面都一樣。拋出一個畸形的錯誤。

簡介

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

它完成了封裝請求,線程切換,數據裝換等一系列工作,如果自己有能力也可以封裝一個這種框架,本質上是沒有區別的。

retrofit使用的網絡引擎是OkHttp.

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

使用
//1 創建網絡請求接口類
public interface GitHubService {
  @GET("users/{user}/repos")
  Call> 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> repos = service.listRepos("octocat");

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

Retrofit也是使用Build模式創建的。

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 adapterFactories = new ArrayList<>(this.adapterFactories);
  adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

  // Make a defensive copy of the converters.
  List 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> listRepos(@Path("user") String user);
}        

從接口文件我們可以看出,一個請求接口被各種注解所表示。

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

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

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

Http請求,首先我們得知道是GET請求還是POST請求,

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

POST請求還需要Body。

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

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

Retrofit 里有個關鍵的類,ServiceMethod

@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
public  T create(final Class 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 serviceMethodCache = new LinkedHashMap<>();

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

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

ServiceMethod
final class ServiceMethod {
  // 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 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 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 {
  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 relativeUrlParamNames;
  ParameterHandler[] parameterHandlers;
  Converter 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 parsePathParameters(String path) {
  Matcher m = PARAM_URL_REGEX.matcher(path);
  Set 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>表示。

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 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 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()
            + ")");
      }
      ParameterizedType parameterizedType = (ParameterizedType) type;
      Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
      Converter converter =
          retrofit.stringConverter(iterableType, annotations);
      return new ParameterHandler.Query<>(name, converter, encoded).iterable();
    } else if (rawParameterType.isArray()) {
      Class arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
      Converter converter =
          retrofit.stringConverter(arrayComponentType, annotations);
      return new ParameterHandler.Query<>(name, converter, encoded).array();
    } else {
      Converter 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)");
    }
    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 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()
            + ")");
      }
      ParameterizedType parameterizedType = (ParameterizedType) type;
      Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
      Converter converter =
          retrofit.stringConverter(iterableType, annotations);
      return new ParameterHandler.Header<>(name, converter).iterable();
    } else if (rawParameterType.isArray()) {
      Class arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
      Converter converter =
          retrofit.stringConverter(arrayComponentType, annotations);
      return new ParameterHandler.Header<>(name, converter).array();
    } else {
      Converter 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)");
    }
    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 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()
            + ")");
      }
      ParameterizedType parameterizedType = (ParameterizedType) type;
      Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
      Converter converter =
          retrofit.stringConverter(iterableType, annotations);
      return new ParameterHandler.Field<>(name, converter, encoded).iterable();
    } else if (rawParameterType.isArray()) {
      Class arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
      Converter converter =
          retrofit.stringConverter(arrayComponentType, annotations);
      return new ParameterHandler.Field<>(name, converter, encoded).array();
    } else {
      Converter 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)");
    }
    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 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()
              + ")");
        }
        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()
              + ")");
        }
        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 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 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 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)");
    }
    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 or a different value type instead.");
    }

    Converter 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 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 extends ParameterHandler {
  private final String name;
  private final Converter valueConverter;

  Header(String name, Converter 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 extends ParameterHandler {
  private final String name; //參數名,占位符
  private final Converter valueConverter;
  private final boolean encoded; //是否編碼

  Path(String name, Converter 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 extends ParameterHandler {
  private final String name;
  private final Converter valueConverter;
  private final boolean encoded;

  Query(String name, Converter 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 extends ParameterHandler> {
  private final Converter valueConverter;
  private final boolean encoded;

  FieldMap(Converter valueConverter, boolean encoded) {
    this.valueConverter = valueConverter;
    this.encoded = encoded;
  }

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

    for (Map.Entry 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 extends ParameterHandler {
  private final String name; //參數名字
  private final Converter valueConverter; //參數值轉換器
  private final boolean encoded; //是否編碼

  Field(String name, Converter 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來表示,然后使用@FieldMap注解來修飾該參數就行了。可想而知,如同@QueryMap一樣,其內部實現肯定是遍歷Map,然后像處理@Field參數一樣調用

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

@Body 使用場景

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

static final class Body extends ParameterHandler {
  private final Converter converter;

  Body(Converter 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 == "	" || codePoint == "
" || codePoint == "f" || codePoint == "
")) {
        // 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[] handlers = (ParameterHandler[]) 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[] handlers = (ParameterHandler[]) 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();
}           
               
                                           
                       
                 
            
                     
             
               

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/72955.html

相關文章

  • Retrofit源碼分析

    摘要:看下圖所示,摘自網絡的創建流程源碼分析實例是使用建造者模式通過類進行創建的。創建了一個含有對象實例的,并返回給源碼分析添加一個調用適配器工廠,用于支持服務方法返回類型注意生產的是,那么又是什么呢可以看到源代碼如下所示,它是一個接口。 目錄介紹 1.首先回顧Retrofit簡單使用方法 2.Retrofit的創建流程源碼分析 2.1 Retrofit對象調用Builder()源碼解...

    zero 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<