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

資訊專欄INFORMATION COLUMN

Retrofit 源碼分析

Kahn / 3499人閱讀

摘要:我們知道一個(gè)方法有一下關(guān)鍵字段組成首先一個(gè)方法必須有描述符,返回值,方法名,參數(shù)類型,參數(shù)構(gòu)成。至此,方法的相關(guān)的注解分析完畢類型的請(qǐng)求,沒(méi)有請(qǐng)求體。方法已將在方法里面分析過(guò)了,這里面都一樣。拋出一個(gè)畸形的錯(cuò)誤。

簡(jiǎn)介

retrofit是square出品的一個(gè)優(yōu)秀的網(wǎng)絡(luò)框架,注意,不是一個(gè)網(wǎng)絡(luò)引擎。它的定位和Volley是一樣的。

它完成了封裝請(qǐng)求,線程切換,數(shù)據(jù)裝換等一系列工作,如果自己有能力也可以封裝一個(gè)這種框架,本質(zhì)上是沒(méi)有區(qū)別的。

retrofit使用的網(wǎng)絡(luò)引擎是OkHttp.

而OKHttp和HTTPClient,HttpUrlConnection是一個(gè)級(jí)別的。

使用
//1 創(chuàng)建網(wǎng)絡(luò)請(qǐng)求接口類
public interface GitHubService {
  @GET("users/{user}/repos")
  Call> listRepos(@Path("user") String user);
}

//2 創(chuàng)建Retrofit實(shí)例對(duì)象
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
     .addConverterFactory(GsonConverterFactory.create())
     .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .build();

//3 通過(guò)動(dòng)態(tài)代理創(chuàng)建網(wǎng)絡(luò)接口代理對(duì)象
GitHubService service = retrofit.create(GitHubService.class);

//4 獲取Call對(duì)象
Call> repos = service.listRepos("octocat");

//5    執(zhí)行同步請(qǐng)求或異步請(qǐng)求
repos.execute();
repos.enqueue(callback)
Retrofit

Retrofit也是使用Build模式創(chuàng)建的。

builder類有這些方法。從圖表可以看出,我們可以調(diào)用client方法傳入一個(gè)我們自定義的OkhttpClient,

調(diào)用baseUrl方法傳入Host,最后調(diào)動(dòng)build方法生成一個(gè)Retrofit 對(duì)象

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

   //如果沒(méi)有設(shè)置callFactory對(duì)象,系統(tǒng)自動(dòng)生成一個(gè)OkhttpClient對(duì)象.因?yàn)镺KHttpclient實(shí)現(xiàn)了            Call.Factory接口
   // public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory
  okhttp3.Call.Factory callFactory = this.callFactory;
  if (callFactory == null) {
    callFactory = new OkHttpClient();
  }

  //如果沒(méi)有設(shè)置callbackExecutor,系統(tǒng)自動(dòng)生成一個(gè),platform.defaultCallbackExecutor,這個(gè)platform是無(wú)參構(gòu)造方法里調(diào)用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 要求必須將請(qǐng)求API寫到一個(gè)interface接口文件里,這是動(dòng)態(tài)代理特性要求的。

從接口文件里我們可以看到,我們將每個(gè)請(qǐng)求用這種形式表達(dá)

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

從接口文件我們可以看出,一個(gè)請(qǐng)求接口被各種注解所表示。

我們知道一個(gè)方法有一下關(guān)鍵字段組成

首先一個(gè)方法必須有描述符,返回值,方法名,參數(shù)類型,參數(shù)構(gòu)成。

那我們用一個(gè)方法表示一個(gè)http請(qǐng)求需要哪些東西呢?

Http請(qǐng)求,首先我們得知道是GET請(qǐng)求還是POST請(qǐng)求,

然后就是請(qǐng)求頭信息,請(qǐng)求路徑,查詢參數(shù)等等。

POST請(qǐng)求還需要Body。

Retrofit 已經(jīng)提供了足夠的注解來(lái)表示一個(gè)方法。

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

Retrofit 里有個(gè)關(guān)鍵的類,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);
          }
            //創(chuàng)建ServiceMethod對(duì)象
          ServiceMethod serviceMethod = loadServiceMethod(method);
          OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
          return serviceMethod.callAdapter.adapt(okHttpCall);
        }
      });
}

從第3步我們可以看出create方法的實(shí)現(xiàn)就是使用了動(dòng)態(tài)代理,在運(yùn)行時(shí)生成了GitHubService對(duì)象。

//創(chuàng)建ServiceMethod對(duì)象
ServiceMethod serviceMethod = loadServiceMethod(method);

ServiceMethod loadServiceMethod(Method method) {
  ServiceMethod result;
  synchronized (serviceMethodCache) {
  //先從換從中取改方法對(duì)應(yīng)的ServiceMethod對(duì)象,如果為null就構(gòu)建一個(gè)ServiceMethod對(duì)象并存入到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)方法返回了一個(gè)ServiceMethod對(duì)象
這個(gè)serviceMethodCache對(duì)象是Retrofit的一個(gè)字段,是一個(gè)Map集合。

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

將接口文件里每個(gè)方法轉(zhuǎn)換為一個(gè)ServiceMethod對(duì)象后放入改map中作為緩存,下次調(diào)用該方法后就不用再次解析改方法對(duì)象了,直接從改map里去以方法為key去取對(duì)應(yīng)的ServiceMethod就行了。666

接下來(lái)看一下ServiceMethod對(duì)象的構(gòu)造

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; 主機(jī)地址
  private final Converter responseConverter;
  private final String httpMethod; 
  private final String relativeUrl; 相對(duì)路徑
  private final Headers headers;    請(qǐng)求頭部信息
  private final MediaType contentType; 請(qǐng)求參數(shù)類型
  private final boolean hasBody;  是否有請(qǐng)求體
  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模式創(chuàng)建的。

static final class Builder {
  final Retrofit retrofit;
  final Method method;         //接口里生命的方法
  final Annotation[] methodAnnotations;  //方法的注解,get/post/header之類的
  final Annotation[][] parameterAnnotationsArray; //方法的參數(shù)注解數(shù)組,二維數(shù)組
  final Type[] parameterTypes;  //方法的參數(shù)數(shù)組

  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(); //獲取被注解修飾的方法,一個(gè)數(shù)組
    this.parameterAnnotationsArray = method.getParameterAnnotations(); //獲取方法的參數(shù)注解信息,是一個(gè)二維數(shù)組
  }

Builder的構(gòu)造參數(shù)需要一個(gè)Retrofit對(duì)象和一個(gè)Method對(duì)象。

首先解析方法對(duì)象,將其注解和參數(shù)注解放到對(duì)應(yīng)的數(shù)組里。

首先在構(gòu)造方法里獲取該方法的注解,方法的參數(shù),以及每個(gè)參數(shù)的注解。

關(guān)鍵就在build方法,在build方法里對(duì)方法做了一個(gè)徹底的分解

public ServiceMethod build() {
  //1 處理返回結(jié)果,做一定的轉(zhuǎn)換
  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,即沒(méi)有使用方法類型注解修飾,拋出異常進(jìn)行提示
  if (httpMethod == null) {
    throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
  }
//如果沒(méi)有請(qǐng)求體,即使用了GET,HEAD,DELETE,OPTIONS等所修飾,即不涉及到表單的提交,但是同時(shí)使用了Multipart,或者FormUrlEncoded所修飾,就報(bào)錯(cuò)
  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提取方法的參數(shù)
  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);
  }

  //相對(duì)路徑為null且gotURL為false的話,拋出異常,因?yàn)闆](méi)有相對(duì)路徑無(wú)法請(qǐng)求。
  if (relativeUrl == null && !gotUrl) {
    throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
  }
  //沒(méi)有使用@FormUrlEncoded,@Multipart主機(jī)并且hasBody為false,但是gotBody為true,拋出異常,提示
    Non-Body類型的HTTP method 不能參數(shù)不能使用@Body注解
  if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
    throw methodError("Non-body HTTP method cannot contain @Body.");
  }
  //使用@FormUrlEncoded修飾的方法中的參數(shù)至少有一個(gè)參數(shù)被@Field注解修飾
  if (isFormEncoded && !gotField) {
    throw methodError("Form-encoded method must contain at least one @Field.");
  }
    
  //使用@Multipart修飾的方法中的參數(shù)至少有一個(gè)參數(shù)被@Part注解修飾
  if (isMultipart && !gotPart) {
    throw methodError("Multipart method must contain at least one @Part.");
  }

 //4 當(dāng)前Builder對(duì)象初始化完畢,可以用來(lái)夠著ServiceMethod對(duì)象。
  return new ServiceMethod<>(this);
}
處理返回結(jié)果
private CallAdapter createCallAdapter() {
  //獲取方法的返回結(jié)果,如果有不能解析的類型則拋出異常,也就是說(shuō)接口中定義的方法的返回值不能使用泛型
  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可能不能正確的處理返回結(jié)果,這時(shí)候拋出異常
    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) {
    //如果同時(shí)使用了FormUrlEncoded注解報(bào)錯(cuò)
      throw methodError("Only one encoding annotation is allowed.");
    }
    isMultipart = true;
  } else if (annotation instanceof FormUrlEncoded) {
    if (isMultipart) {
    //如果同時(shí)使用了Multipart注解報(bào)錯(cuò),從這我們可以看出一個(gè)方法不能同時(shí)被Multipart和FormUrlEncoded所修飾
      throw methodError("Only one encoding annotation is allowed.");
    }
    isFormEncoded = true;
  }
}

然后根據(jù)具體的注解類型,在做進(jìn)一步的處理,這里主要分析GET/POST/HEADER/ 等注解

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

get類型的請(qǐng)求,沒(méi)有請(qǐng)求體

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

    //如果value為null,返回,因?yàn)関alue參數(shù)的值其實(shí)就是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);
      //獲取查詢參數(shù)
    Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
    if (queryParamMatcher.find()) {
        //如果在value里面找到里查詢參數(shù)的話,拋出異常。因?yàn)椴樵儏?shù)可以使用@Query注解來(lái)動(dòng)態(tài)配置。
      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占位符,如果有的話
}

再來(lái)看下解析value里的path占位符的方法。

/**
獲取已知URI里面的路徑集合,如果一個(gè)參數(shù)被使用了兩次,它只會(huì)在set中出現(xiàn)一次,好拗口啊,使用LinkedHashSet來(lái)保存path參數(shù)集合,保證了路徑參數(shù)的順序。
 * 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方法的相關(guān)的注解分析完畢

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

POST類型的請(qǐng)求,沒(méi)有請(qǐng)求體。所以hasBody參數(shù)為true。

parseHttpMethodAndPath()方法已將在GET方法里面分析過(guò)了,這里面都一樣。

其他的請(qǐng)求類型也是大同小異。

然后接著分析方法的Header注解

@Headers
else if (annotation instanceof retrofit2.http.Headers) {
 //   首先獲取Headers注解的值,是一個(gè)字符串?dāng)?shù)組。
  String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
    如果header注解長(zhǎng)度為0,拋出異常,所以使用了header注解必須設(shè)置值,不能存在空的header
  if (headersToParse.length == 0) {
    throw methodError("@Headers annotation is empty.");
  }
    處理header信息,我猜肯定是一個(gè)map
  headers = parseHeaders(headersToParse);

啊,居然不是,666.因?yàn)閔eader不是KV結(jié)構(gòu)的數(shù)據(jù)類型,而是一個(gè)key可以對(duì)應(yīng)多個(gè)值。理論上可以使用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格式表示,不然報(bào)錯(cuò)
      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值,必須是一個(gè)數(shù)組,艸,又猜錯(cuò)了。
    if ("Content-Type".equalsIgnoreCase(headerName)) {
    //遇到"Content-Type"字段。還需要獲得具體的MediaType。
      MediaType type = MediaType.parse(headerValue);
      if (type == null) {
      //如果mediaType為null。拋出一個(gè)type畸形的錯(cuò)誤。
        throw methodError("Malformed content type: %s", headerValue);
      }
      contentType = type;
    } else {
    將header的key和value加入到Builder里面。
      builder.add(headerName, headerValue);
    }
  }
  最后調(diào)用build方法生成一個(gè)Header對(duì)愛(ài)。
  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類的一種子段。可見(jiàn)在Builder內(nèi)部header信息是按照key/value異常放到一個(gè)String集合里面的。為什么不放到一個(gè)Map里面呢,不懂。

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

處理方法參數(shù)
int parameterCount = parameterAnnotationsArray.length; //求得數(shù)組的長(zhǎng)度
parameterHandlers = new ParameterHandler[parameterCount];
for (int p = 0; p < parameterCount; p++) {
  Type parameterType = parameterTypes[p]; //便利參數(shù),依次處理參數(shù)
    //如果參數(shù)不能解析,拋出異常
  if (Utils.hasUnresolvableType(parameterType)) {
    throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
        parameterType);
  }
//獲取第p個(gè)參數(shù)的注解數(shù)組,如果沒(méi)有注解拋出異常,可見(jiàn),使用了Retrofit,接口方法中每個(gè)參數(shù)都必須使用注解進(jìn)行修飾。
  Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
  if (parameterAnnotations == null) {
    throw parameterError(p, "No Retrofit annotation found.");
  }

   //解析方法中的參數(shù),存入parameterHandlers[]數(shù)組中。
  parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
參數(shù)校驗(yàn)

Utils.hasUnresolvableType(parameterType),這個(gè)方法是對(duì)參數(shù)的類型做個(gè)校驗(yàn)。

static boolean hasUnresolvableType(Type type) {
  //如果參數(shù)是引用數(shù)據(jù)類型,返回false,可見(jiàn),接口定義中方法的參數(shù)只能是基本數(shù)據(jù)類型
  if (type instanceof Class) {
    return false;
  }
  //如果參數(shù)是泛型
  if (type instanceof ParameterizedType) {
    ParameterizedType parameterizedType = (ParameterizedType) type;
    //去除泛型類中的實(shí)際類型,遍歷
    for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
    //如果有一個(gè)泛型參數(shù)是基本數(shù)據(jù)類型,返回true,都不是返回false
      if (hasUnresolvableType(typeArgument)) {
        return true;
      }
    }
    return false;
  }
  //如果參數(shù)是泛型數(shù)組類型
  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);
}
解析參數(shù)
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
private ParameterHandler parseParameter(
        int p, Type parameterType, Annotation[] annotations) {
      ParameterHandler result = null;
    //遍歷參數(shù)的注解數(shù)組,調(diào)用parseParameterAnnotation()
      for (Annotation annotation : annotations) {
        ParameterHandler annotationAction = parseParameterAnnotation(
            p, parameterType, annotations, annotation);
        //如果該注解沒(méi)有返回,則解析下一個(gè)注解
        if (annotationAction == null) {
          continue;
        }

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

    //如果注解為null,拋出異常。這個(gè)地方永遠(yuǎn)不會(huì)調(diào)用,因?yàn)樵讷@取注解數(shù)組之前就做過(guò)判斷了,如果注解數(shù)組為null,直接拋異常,Line197-Line200 in ServiceMethod.Builder中
      if (result == null) {
        throw parameterError(p, "No Retrofit annotation found.");
      }

      return result;
    }
獲取參數(shù)注解信息

再來(lái)看看parseParameterAnnotation()方法,內(nèi)容略多

private ParameterHandler parseParameterAnnotation(
    int p, Type type, Annotation[] annotations, Annotation annotation) {
  if (annotation instanceof Url) {
      //如果使用了Url注解,
    if (gotUrl) {
        //如果gotUrl為true,因?yàn)間otURL默認(rèn)為false,說(shuō)明之前處理過(guò)Url注解了,拋出多個(gè)@Url注解異常
      throw parameterError(p, "Multiple @Url method annotations found.");
    }
    if (gotPath) {
        //如果gotPath為true,拋出異常,說(shuō)明@Path注解不能和@Url注解一起使用
      throw parameterError(p, "@Path parameters may not be used with @Url.");
    }
    if (gotQuery) {
        //如果gotQuery為true,拋出異常,說(shuō)明@Url注解不能用在@Query注解后面
      throw parameterError(p, "A @Url parameter must not come after a @Query");
    }
    if (relativeUrl != null) {
        //如果relativeUrl不為null,拋出異常,說(shuō)明使用了@Url注解,relativeUrl必須為null
      throw parameterError(p, "@Url cannot be used with @%s URL", httpMethod);
    }

    gotUrl = true;
      
----------------------------------------------------------------------------------------    
      //如果參數(shù)類型是HttpURL,String,URI或者參數(shù)類型是“android.net.Uri",返回ParameterHandler.RelativeUrl(),實(shí)際是交由這個(gè)類處理
    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 {
        //不然就拋出異常,也就是說(shuō)@Url注解必須使用在okhttp3.HttpUrl, String, java.net.URI, or android.net.Uri 這幾種類型的參數(shù)上。
      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。拋出異常,因?yàn)锧Path修飾的參數(shù)是路徑的占位符。不是查詢參數(shù),不能使用@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.");
    }
      //如果相對(duì)路徑為null,那@path注解也就無(wú)意義了。
    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); //對(duì)改值進(jìn)行校驗(yàn),1該value必須是合法字符,2:該相對(duì)路徑必須包含相應(yīng)的占位符
 
      //然后將改參數(shù)的所有注解進(jìn)行處理,最終調(diào)用ParameterHandler.Path進(jìn)行處理。
    Converter converter = retrofit.stringConverter(type, annotations);
    return new ParameterHandler.Path<>(name, converter, path.encoded());

  } else if (annotation instanceof Query) { //Query注解,看不太懂,最后也是調(diào)用ParameterHandler.Query進(jìn)行處理
    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.找不到該注解
}

從上面可以看出,改立參數(shù)注解的套路就是:先判斷該注解的類型,然后使用策略模式分別調(diào)用ParameterHandler里對(duì)應(yīng)的子類來(lái)處理

寫到這里我已經(jīng)暈了。暈暈乎乎好舒服

@Header 使用場(chǎng)景

有時(shí)候我們需要?jiǎng)討B(tài)的設(shè)置請(qǐng)求header中的某個(gè)請(qǐng)求頭的值,這個(gè)時(shí)候就可以使用@Header來(lái)修飾個(gè)參數(shù)。

最終都是講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);
  }
}

調(diào)用requestBuilder.addHeader()方法。

這個(gè)requestBuilder是OKHttp中Request的內(nèi)部靜態(tài)類Builder類的一個(gè)對(duì)象。

private final Request.Builder requestBuilder;

從中我們可以看出最后將@Header注釋的參數(shù)的值解析后添加到Request對(duì)象中的Header信息里。

@Path 使用場(chǎng)景

有時(shí)候請(qǐng)求路徑是不定的,即請(qǐng)求路徑里的某個(gè)segment是變化的,也就是需要我們使用參數(shù)來(lái)動(dòng)態(tài)的改變,這個(gè)時(shí)候我們就需要使用@Path 來(lái)修飾這個(gè)參數(shù)

static final class Path extends ParameterHandler {
  private final String name; //參數(shù)名,占位符
  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 使用場(chǎng)景

@Query用來(lái)修飾接口方法中的查詢字段

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);
  }
}
//將查詢參數(shù)組合到相對(duì)路徑上。
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 使用場(chǎng)景

當(dāng)接口中的一個(gè) 方法有比較多的查詢字段時(shí),全部定義到方法中時(shí)比較麻煩且容易出錯(cuò),這個(gè)使用我們完全可以將所有的查詢參數(shù)放到一個(gè)Map里面。

可想而知,其內(nèi)部實(shí)現(xiàn)必定是遍歷map ,然后像處理@Query參數(shù)一樣調(diào)用addQueryParam()處理每個(gè)查詢參數(shù)。

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 使用場(chǎng)景

@Field注解一般用在表單參數(shù)的提交上

static final class Field extends ParameterHandler {
  private final String name; //參數(shù)名字
  private final Converter valueConverter; //參數(shù)值轉(zhuǎn)換器
  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修飾的字段,是不會(huì)上傳到服務(wù)器的。
    //調(diào)用ResuestBuilder對(duì)象的具體想法來(lái)處理@Field修飾的表單字段
    builder.addFormField(name, valueConverter.convert(value), encoded);
  }
}
void addFormField(String name, String value, boolean encoded) {
//根據(jù)參數(shù)值是否被編碼,調(diào)用不同的方法。formBuilder是OKHttp中的一個(gè)類。也是使用Builder模式創(chuàng)建的。
  if (encoded) {
    formBuilder.addEncoded(name, value);
  } else {
    formBuilder.add(name, value);
  }
}
@FieldMap

@FieldMap

使用場(chǎng)景

假如表單參數(shù)有很多個(gè),我們可以使用一個(gè)Map來(lái)表示,然后使用@FieldMap注解來(lái)修飾該參數(shù)就行了。可想而知,如同@QueryMap一樣,其內(nèi)部實(shí)現(xiàn)肯定是遍歷Map,然后像處理@Field參數(shù)一樣調(diào)用

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

@Body 使用場(chǎng)景

在以下需要提交表單的請(qǐng)求里,我們可以使用@Field,@FieldMap,我們還可以使用@Body來(lái)修飾我們提交的表單數(shù)據(jù),這個(gè)時(shí)候我們需要定義一個(gè)Bean類,Bean類的各個(gè)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并沒(méi)有像@Field一樣處理表單參數(shù)。仔細(xì)想想也對(duì),因?yàn)榉彩翘峤坏谋韱螖?shù)據(jù)都需要放到請(qǐng)求體里面,即使使用@Field,@FieldMap提交的數(shù)據(jù),最終還是需要放到請(qǐng)求體里面。

@Part @RawPart @PartMap

以上三個(gè)注解都是使用修飾上傳文件的參數(shù)的,

結(jié)論

從對(duì)上面的分析可以知道,我們?cè)谔崛∈褂米⒔庑揎椀膮?shù)后將值存放到RequestBuilder對(duì)象里。

這里又引入了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;     //相對(duì)路徑
  private HttpUrl.Builder urlBuilder; //URL構(gòu)造器

  private final Request.Builder requestBuilder; //OkHttp中Request構(gòu)造器
  private MediaType contentType;        //提交表單的數(shù)據(jù)類型

  private final boolean hasBody;        //是否有請(qǐng)求體
  private MultipartBody.Builder multipartBuilder; //上傳文件的構(gòu)造器
  private FormBody.Builder formBuilder;                //表單數(shù)據(jù)的構(gòu)造器
  private RequestBody body;                            //請(qǐng)求體

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

    //生成一個(gè)Request對(duì)象
    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);

在創(chuàng)建了ServiceMethod對(duì)象后,使用該ServiceMethod對(duì)象和其參數(shù)創(chuàng)建一個(gè)OKHttPCall對(duì)象

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

在合適的時(shí)候調(diào)用ServiceMethod對(duì)象的toRequest方法生成一個(gè)Request對(duì)象,toReques()的內(nèi)部實(shí)現(xiàn)就是調(diào)用RequestBuilder對(duì)象的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 實(shí)現(xiàn)了Call接口,這個(gè)Call接口和OkHttp中的Call接口一樣,畢竟一家公司嘛。

其實(shí)就是對(duì)OkHttpCall 做了一層包裝。

最后方法的執(zhí)行時(shí)通過(guò)調(diào)用

return serviceMethod.callAdapter.adapt(okHttpCall);

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

這塊的流程就是構(gòu)造一個(gè)OKHttp對(duì)象需要使用ServiceMethod對(duì)象和相應(yīng)的參數(shù)。

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

最后創(chuàng)建具體的Call對(duì)象時(shí)

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

調(diào)用了ServiceMethod對(duì)象的toRequest方法,然后使用這個(gè)request對(duì)象創(chuàng)建了一個(gè)Call對(duì)象。

/** 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]);
  }
//生成一個(gè)Request對(duì)象
  return requestBuilder.build();
}           
               
                                           
                       
                 
            
                     
             
               

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/72955.html

相關(guān)文章

  • Retrofit源碼分析

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

    zero 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<