retrofit是square出品的一個優秀的網絡框架,注意,不是一個網絡引擎。它的定位和Volley是同樣的。java
它完成了封裝請求,線程切換,數據裝換等一系列工做,若是本身有能力也能夠封裝一個這種框架,本質上是沒有區別的。android
retrofit使用的網絡引擎是OkHttp.git
而OKHttp和HTTPClient,HttpUrlConnection是一個級別的。github
//1 建立網絡請求接口類 public interface GitHubService { @GET("users/{user}/repos") Call<List<Repo>> listRepos(@Path("user") String user); } //2 建立Retrofit實例對象 Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); //3 經過動態代理建立網絡接口代理對象 GitHubService service = retrofit.create(GitHubService.class); //4 獲取Call對象 Call<List<Repo>> repos = service.listRepos("octocat"); //5 執行同步請求或異步請求 repos.execute(); repos.enqueue(callback)
Retrofit也是使用Build模式建立的。api
builder類有這些方法。從圖表能夠看出,咱們能夠調用client方法傳入一個咱們自定義的OkhttpClient,數組
調用baseUrl方法傳入Host,最後調動build方法生成一個Retrofit 對象緩存
public Retrofit build() { //baseUrl是必須的 if (baseUrl == null) { throw new IllegalStateException("Base URL required."); } //若是沒有設置callFactory對象,系統自動生成一個OkhttpClient對象.由於OKHttpclient實現了 Call.Factory接口 // public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { callFactory = new OkHttpClient(); } //若是沒有設置callbackExecutor,系統自動生成一個,platform.defaultCallbackExecutor,這個platform是無參構造方法裏調用Platform.get()方法獲得的。 /** public Builder() { this(Platform.get()); }**/ Executor callbackExecutor = this.callbackExecutor; if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); } // Make a defensive copy of the adapters and add the default Call adapter. List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); // Make a defensive copy of the converters. List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories); return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); }
class Platform { private static final Platform PLATFORM = findPlatform(); static Platform get() { return PLATFORM; } private static Platform findPlatform() { try { Class.forName("android.os.Build"); if (Build.VERSION.SDK_INT != 0) { return new Android(); } } catch (ClassNotFoundException ignored) { } try { Class.forName("java.util.Optional"); return new Java8(); } catch (ClassNotFoundException ignored) { } try { //怎麼還有IOS代碼呢? Class.forName("org.robovm.apple.foundation.NSObject"); return new IOS(); } catch (ClassNotFoundException ignored) { } return new Platform(); } Executor defaultCallbackExecutor() { return null; } CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) { if (callbackExecutor != null) { return new ExecutorCallAdapterFactory(callbackExecutor); } return DefaultCallAdapterFactory.INSTANCE; } boolean isDefaultMethod(Method method) { return false; } Object invokeDefaultMethod(Method method, Class<?> declaringClass, Object object, Object... args) throws Throwable { throw new UnsupportedOperationException(); } static class Android extends Platform { @Override public Executor defaultCallbackExecutor() { return new MainThreadExecutor(); } @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) { return new ExecutorCallAdapterFactory(callbackExecutor); } static class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable r) { handler.post(r); } } } }
Retrofit 要求必須將請求API寫到一個interface接口文件裏,這是動態代理特性要求的。服務器
從接口文件裏咱們能夠看到,咱們將每一個請求用這種形式表達網絡
public interface GitHubService { @GET("users/{user}/repos") Call<List<Repo>> listRepos(@Path("user") String user); }
從接口文件咱們能夠看出,一個請求接口被各類註解所表示。app
咱們知道一個方法有一下關鍵字段組成
首先一個方法必須有描述符,返回值,方法名,參數類型,參數構成。
那咱們用一個方法表示一個http請求須要哪些東西呢?
Http請求,首先咱們得知道是GET請求仍是POST請求,
而後就是請求頭信息,請求路徑,查詢參數等等。
POST請求還須要Body。
Retrofit 已經提供了足夠的註解來表示一個方法。
Retrofit的核心思想AOP,面向切面變成,經過動態代理的反射,將接口文件裏的每一個方法記性處理,也就是分析該方法的註解生成一個ServiceMethod類。
Retrofit 裏有個關鍵的類,ServiceMethod
@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety. public <T> T create(final Class<T> service) { Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); } return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { private final Platform platform = Platform.get(); @Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable { // If the method is a method from Object then defer to normal invocation. if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } //建立ServiceMethod對象 ServiceMethod serviceMethod = loadServiceMethod(method); OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); } }); }
從第3步咱們能夠看出create方法的實現就是使用了動態代理,在運行時生成了GitHubService對象。
//建立ServiceMethod對象
ServiceMethod serviceMethod = loadServiceMethod(method);
ServiceMethod loadServiceMethod(Method method) { ServiceMethod result; synchronized (serviceMethodCache) { //先從換從中取改方法對應的ServiceMethod對象,若是爲null就構建一個ServiceMethod對象並存入到map中,若是不爲null直接返回 result = serviceMethodCache.get(method); if (result == null) { result = new ServiceMethod.Builder(this, method).build(); serviceMethodCache.put(method, result); } } return result; }
咱們能夠看到loadServiceMethod(Method method)方法返回了一個ServiceMethod對象
這個serviceMethodCache對象是Retrofit的一個字段,是一個Map集合。
private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();
將接口文件裏每一個方法轉換爲一個ServiceMethod對象後放入改map中做爲緩存,下次調用該方法後就不用再次解析改方法對象了,直接從改map裏去以方法爲key去取對應的ServiceMethod就好了。666
接下來看一下ServiceMethod對象的構造
final class ServiceMethod<T> { // Upper and lower characters, digits, underscores, and hyphens, starting with a character. static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*"; static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}"); static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM); final okhttp3.Call.Factory callFactory; final CallAdapter<?> callAdapter; private final HttpUrl baseUrl; 主機地址 private final Converter<ResponseBody, T> responseConverter; private final String httpMethod; private final String relativeUrl; 相對路徑 private final Headers headers; 請求頭部信息 private final MediaType contentType; 請求參數類型 private final boolean hasBody; 是否有請求體 private final boolean isFormEncoded; 是不是格式化的表單 private final boolean isMultipart; 是否是分塊 private final ParameterHandler<?>[] parameterHandlers; ServiceMethod(Builder<T> builder) { this.callFactory = builder.retrofit.callFactory(); this.callAdapter = builder.callAdapter; this.baseUrl = builder.retrofit.baseUrl(); this.responseConverter = builder.responseConverter; this.httpMethod = builder.httpMethod; this.relativeUrl = builder.relativeUrl; this.headers = builder.headers; this.contentType = builder.contentType; this.hasBody = builder.hasBody; this.isFormEncoded = builder.isFormEncoded; this.isMultipart = builder.isMultipart; this.parameterHandlers = builder.parameterHandlers; } }
ServiceMethod是採用Builder模式建立的。
static final class Builder<T> { final Retrofit retrofit; final Method method; //接口裏生命的方法 final Annotation[] methodAnnotations; //方法的註解,get/post/header之類的 final Annotation[][] parameterAnnotationsArray; //方法的參數註解數組,二維數組 final Type[] parameterTypes; //方法的參數數組 Type responseType; boolean gotField; boolean gotPart; boolean gotBody; boolean gotPath; boolean gotQuery; boolean gotUrl; String httpMethod; boolean hasBody; boolean isFormEncoded; boolean isMultipart; String relativeUrl; Headers headers; MediaType contentType; Set<String> relativeUrlParamNames; ParameterHandler<?>[] parameterHandlers; Converter<ResponseBody, T> responseConverter; CallAdapter<?> callAdapter; public Builder(Retrofit retrofit, Method method) { this.retrofit = retrofit; this.method = method; this.methodAnnotations = method.getAnnotations(); //獲取方法的註解 this.parameterTypes = method.getGenericParameterTypes(); //獲取被註解修飾的方法,一個數組 this.parameterAnnotationsArray = method.getParameterAnnotations(); //獲取方法的參數註解信息,是一個二維數組 }
Builder的構造參數須要一個Retrofit對象和一個Method對象。
首先解析方法對象,將其註解和參數註解放到對應的數組裏。
首先在構造方法裏獲取該方法的註解,方法的參數,以及每一個參數的註解。
關鍵就在build方法,在build方法裏對方法作了一個完全的分解
public ServiceMethod build() { //1 處理返回結果,作必定的轉換 callAdapter = createCallAdapter(); responseType = callAdapter.responseType(); if (responseType == Response.class || responseType == okhttp3.Response.class) { throw methodError("'" + Utils.getRawType(responseType).getName() + "' is not a valid response body type. Did you mean ResponseBody?"); } responseConverter = createResponseConverter(); //2提取方法的註解 for (Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation); } //若是httpMethod爲null,即沒有使用方法類型註解修飾,拋出異常進行提示 if (httpMethod == null) { throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.)."); } //若是沒有請求體,即便用了GET,HEAD,DELETE,OPTIONS等所修飾,即不涉及到表單的提交,可是同時使用了Multipart,或者FormUrlEncoded所修飾,就報錯 if (!hasBody) { if (isMultipart) { throw methodError( "Multipart can only be specified on HTTP methods with request body (e.g., @POST)."); } if (isFormEncoded) { throw methodError("FormUrlEncoded can only be specified on HTTP methods with " + "request body (e.g., @POST)."); } } //3提取方法的參數 int parameterCount = parameterAnnotationsArray.length; parameterHandlers = new ParameterHandler<?>[parameterCount]; for (int p = 0; p < parameterCount; p++) { Type parameterType = parameterTypes[p]; if (Utils.hasUnresolvableType(parameterType)) { throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s", parameterType); } Annotation[] parameterAnnotations = parameterAnnotationsArray[p]; if (parameterAnnotations == null) { throw parameterError(p, "No Retrofit annotation found."); } parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations); } //相對路徑爲null且gotURL爲false的話,拋出異常,由於沒有相對路徑沒法請求。 if (relativeUrl == null && !gotUrl) { throw methodError("Missing either @%s URL or @Url parameter.", httpMethod); } //沒有使用@FormUrlEncoded,@Multipart主機而且hasBody爲false,可是gotBody爲true,拋出異常,提示 Non-Body類型的HTTP method 不能參數不能使用@Body註解 if (!isFormEncoded && !isMultipart && !hasBody && gotBody) { throw methodError("Non-body HTTP method cannot contain @Body."); } //使用@FormUrlEncoded修飾的方法中的參數至少有一個參數被@Field註解修飾 if (isFormEncoded && !gotField) { throw methodError("Form-encoded method must contain at least one @Field."); } //使用@Multipart修飾的方法中的參數至少有一個參數被@Part註解修飾 if (isMultipart && !gotPart) { throw methodError("Multipart method must contain at least one @Part."); } //4 當前Builder對象初始化完畢,能夠用來夠着ServiceMethod對象。 return new ServiceMethod<>(this); }
private CallAdapter<?> createCallAdapter() { //獲取方法的返回結果,若是有不能解析的類型則拋出異常,也就是說接口中定義的方法的返回值不能使用泛型 Type returnType = method.getGenericReturnType(); if (Utils.hasUnresolvableType(returnType)) { throw methodError( "Method return type must not include a type variable or wildcard: %s", returnType); } //接口裏的方法不能返回void if (returnType == void.class) { throw methodError("Service methods cannot return void."); } Annotation[] annotations = method.getAnnotations(); try { return retrofit.callAdapter(returnType, annotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. //用戶自定義的Adapter可能不能正確的處理返回結果,這時候拋出異常 throw methodError(e, "Unable to create call adapter for %s", returnType); } }
1到處理方法的註解,就是先處理GET/POST/Header等註解信息
private void parseMethodAnnotation(Annotation annotation) { if (annotation instanceof DELETE) { parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false); } else if (annotation instanceof GET) { parseHttpMethodAndPath("GET", ((GET) annotation).value(), false); } else if (annotation instanceof HEAD) { parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false); if (!Void.class.equals(responseType)) { throw methodError("HEAD method must use Void as response type."); } } else if (annotation instanceof PATCH) { parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true); } else if (annotation instanceof POST) { parseHttpMethodAndPath("POST", ((POST) annotation).value(), true); } else if (annotation instanceof PUT) { parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true); } else if (annotation instanceof OPTIONS) { parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false); } else if (annotation instanceof HTTP) { HTTP http = (HTTP) annotation; parseHttpMethodAndPath(http.method(), http.path(), http.hasBody()); } else if (annotation instanceof retrofit2.http.Headers) { headers註解 String[] headersToParse = ((retrofit2.http.Headers) annotation).value(); if (headersToParse.length == 0) { throw methodError("@Headers annotation is empty."); } headers = parseHeaders(headersToParse); } else if (annotation instanceof Multipart) {//若是是Multipart註解 if (isFormEncoded) { //若是同時使用了FormUrlEncoded註解報錯 throw methodError("Only one encoding annotation is allowed."); } isMultipart = true; } else if (annotation instanceof FormUrlEncoded) { if (isMultipart) { //若是同時使用了Multipart註解報錯,從這咱們能夠看出一個方法不能同時被Multipart和FormUrlEncoded所修飾 throw methodError("Only one encoding annotation is allowed."); } isFormEncoded = true; } }
而後根據具體的註解類型,在作進一步的處理,這裏主要分析GET/POST/HEADER/ 等註解
else if (annotation instanceof GET) { parseHttpMethodAndPath("GET", ((GET) annotation).value(), false); }
get類型的請求,沒有請求體
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) { //若是該Builder已經有HTTPMethod了就不能改變了,直接拋異常 if (this.httpMethod != null) { throw methodError("Only one HTTP method is allowed. Found: %s and %s.", this.httpMethod, httpMethod); } //將HTTPMethod賦值給httpMethod對象,Get、Post、Delete等 this.httpMethod = httpMethod; this.hasBody = hasBody;//是否有請求體 //若是value爲null,返回,由於value參數的值其實就是relativeURL。因此不能爲null if (value.isEmpty()) { return; } // Get the relative URL path and existing query string, if present. int question = value.indexOf('?'); if (question != -1 && question < value.length() - 1) { // Ensure the query string does not have any named parameters. String queryParams = value.substring(question + 1); //獲取查詢參數 Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams); if (queryParamMatcher.find()) { //若是在value裏面找到裏查詢參數的話,拋出異常。由於查詢參數可使用@Query註解來動態配置。 throw methodError("URL query string \"%s\" must not have replace block. " + "For dynamic query parameters use @Query.", queryParams); } } this.relativeUrl = value; //將value賦值給relativeUrl this.relativeUrlParamNames = parsePathParameters(value); //獲取value裏面的path佔位符,若是有的話 }
再來看下解析value裏的path佔位符的方法。
/** 獲取已知URI裏面的路徑集合,若是一個參數被使用了兩次,它只會在set中出現一次,好拗口啊,使用LinkedHashSet來保存path參數集合,保證了路徑參數的順序。 * Gets the set of unique path parameters used in the given URI. If a parameter is used twice * in the URI, it will only show up once in the set. */ static Set<String> parsePathParameters(String path) { Matcher m = PARAM_URL_REGEX.matcher(path); Set<String> patterns = new LinkedHashSet<>(); while (m.find()) { patterns.add(m.group(1)); } return patterns; }
至此,GET方法的相關的註解分析完畢
else if (annotation instanceof POST) { parseHttpMethodAndPath("POST", ((POST) annotation).value(), true); }
POST類型的請求,沒有請求體。因此hasBody參數爲true。
parseHttpMethodAndPath()方法已將在GET方法裏面分析過了,這裏面都同樣。
其餘的請求類型也是大同小異。
而後接着分析方法的Header註解
else if (annotation instanceof retrofit2.http.Headers) { // 首先獲取Headers註解的值,是一個字符串數組。 String[] headersToParse = ((retrofit2.http.Headers) annotation).value(); 若是header註解長度爲0,拋出異常,因此使用了header註解必須設置值,不能存在空的header if (headersToParse.length == 0) { throw methodError("@Headers annotation is empty."); } 處理header信息,我猜確定是一個map headers = parseHeaders(headersToParse);
啊,竟然不是,666.由於header不是KV結構的數據類型,而是一個key能夠對應多個值。理論上可使用Map<String,Set<String>>表示。
private Headers parseHeaders(String[] headers) { Headers.Builder builder = new Headers.Builder(); for (String header : headers) { // header以「:"分割,前面是key,後面是value int colon = header.indexOf(':'); if (colon == -1 || colon == 0 || colon == header.length() - 1) { //header必須是key:value格式表示,否則報錯 throw methodError( "@Headers value must be in the form \"Name: Value\". Found: \"%s\"", header); } String headerName = header.substring(0, colon); //key值 String headerValue = header.substring(colon + 1).trim(); //value值,必須是一個數組,艹,又猜錯了。 if ("Content-Type".equalsIgnoreCase(headerName)) { //遇到"Content-Type"字段。還須要得到具體的MediaType。 MediaType type = MediaType.parse(headerValue); if (type == null) { //若是mediaType爲null。拋出一個type畸形的錯誤。 throw methodError("Malformed content type: %s", headerValue); } contentType = type; } else { 將header的key和value加入到Builder裏面。 builder.add(headerName, headerValue); } } 最後調用build方法生成一個Header對愛。 return builder.build(); }
/** * Add a header with the specified name and value. Does validation of header names and values. */ public Builder add(String name, String value) { checkNameAndValue(name, value); return addLenient(name, value); }
Builder addLenient(String name, String value) { namesAndValues.add(name); namesAndValues.add(value.trim()); return this; }
final List<String> namesAndValues = new ArrayList<>(20);
namesAndValues是Header.Builder類的一種子段。可見在Builder內部header信息是按照key/value異常放到一個String集合裏面的。爲何不放到一個Map裏面呢,不懂。
總之,最後就是講方法的Headers註解信息提取完畢。
int parameterCount = parameterAnnotationsArray.length; //求得數組的長度 parameterHandlers = new ParameterHandler<?>[parameterCount]; for (int p = 0; p < parameterCount; p++) { Type parameterType = parameterTypes[p]; //便利參數,依次處理參數 //若是參數不能解析,拋出異常 if (Utils.hasUnresolvableType(parameterType)) { throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s", parameterType); } //獲取第p個參數的註解數組,若是沒有註解拋出異常,可見,使用了Retrofit,接口方法中每一個參數都必須使用註解進行修飾。 Annotation[] parameterAnnotations = parameterAnnotationsArray[p]; if (parameterAnnotations == null) { throw parameterError(p, "No Retrofit annotation found."); } //解析方法中的參數,存入parameterHandlers[]數組中。 parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations); }
Utils.hasUnresolvableType(parameterType),這個方法是對參數的類型作個校驗。
static boolean hasUnresolvableType(Type type) { //若是參數是引用數據類型,返回false,可見,接口定義中方法的參數只能是基本數據類型 if (type instanceof Class<?>) { return false; } //若是參數是泛型 if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; //去除泛型類中的實際類型,遍歷 for (Type typeArgument : parameterizedType.getActualTypeArguments()) { //若是有一個泛型參數是基本數據類型,返回true,都不是返回false if (hasUnresolvableType(typeArgument)) { return true; } } return false; } //若是參數是泛型數組類型 if (type instanceof GenericArrayType) { return hasUnresolvableType(((GenericArrayType) type).getGenericComponentType()); } if (type instanceof TypeVariable) { return true; } if (type instanceof WildcardType) { return true; } String className = type == null ? "null" : type.getClass().getName(); throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " + "GenericArrayType, but <" + type + "> is of type " + className); }
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations); private ParameterHandler<?> parseParameter( int p, Type parameterType, Annotation[] annotations) { ParameterHandler<?> result = null; //遍歷參數的註解數組,調用parseParameterAnnotation() for (Annotation annotation : annotations) { ParameterHandler<?> annotationAction = parseParameterAnnotation( p, parameterType, annotations, annotation); //若是該註解沒有返回,則解析下一個註解 if (annotationAction == null) { continue; } if (result != null) { throw parameterError(p, "Multiple Retrofit annotations found, only one allowed."); } result = annotationAction; //將解析的結果賦值給Result } //若是註解爲null,拋出異常。這個地方永遠不會調用,由於在獲取註解數組以前就作過判斷了,若是註解數組爲null,直接拋異常,Line197-Line200 in ServiceMethod.Builder中 if (result == null) { throw parameterError(p, "No Retrofit annotation found."); } return result; }
再來看看parseParameterAnnotation()方法,內容略多
private ParameterHandler<?> parseParameterAnnotation( int p, Type type, Annotation[] annotations, Annotation annotation) { if (annotation instanceof Url) { //若是使用了Url註解, if (gotUrl) { //若是gotUrl爲true,由於gotURL默認爲false,說明以前處理過Url註解了,拋出多個@Url註解異常 throw parameterError(p, "Multiple @Url method annotations found."); } if (gotPath) { //若是gotPath爲true,拋出異常,說明@Path註解不能和@Url註解一塊兒使用 throw parameterError(p, "@Path parameters may not be used with @Url."); } if (gotQuery) { //若是gotQuery爲true,拋出異常,說明@Url註解不能用在@Query註解後面 throw parameterError(p, "A @Url parameter must not come after a @Query"); } if (relativeUrl != null) { //若是relativeUrl不爲null,拋出異常,說明使用了@Url註解,relativeUrl必須爲null throw parameterError(p, "@Url cannot be used with @%s URL", httpMethod); } gotUrl = true; ---------------------------------------------------------------------------------------- //若是參數類型是HttpURL,String,URI或者參數類型是「android.net.Uri",返回ParameterHandler.RelativeUrl(),實際是交由這個類處理 if (type == HttpUrl.class || type == String.class || type == URI.class || (type instanceof Class && "android.net.Uri".equals(((Class<?>) type).getName()))) { return new ParameterHandler.RelativeUrl(); } else { //否則就拋出異常,也就是說@Url註解必須使用在okhttp3.HttpUrl, String, java.net.URI, or android.net.Uri 這幾種類型的參數上。 throw parameterError(p, "@Url must be okhttp3.HttpUrl, String, java.net.URI, or android.net.Uri type."); } ------------------------------------------------------------------------------------------ } else if (annotation instanceof Path) { //@Path註解 //若是gotQuery爲true。拋出異常,由於@Path修飾的參數是路徑的佔位符。不是查詢參數,不能使用@Query註解修飾 if (gotQuery) { throw parameterError(p, "A @Path parameter must not come after a @Query."); } if (gotUrl) { throw parameterError(p, "@Path parameters may not be used with @Url."); } //若是相對路徑爲null,那@path註解也就無心義了。 if (relativeUrl == null) { throw parameterError(p, "@Path can only be used with relative url on @%s", httpMethod); } gotPath = true; Path path = (Path) annotation; String name = path.value(); //獲取@Path註解的值 validatePathName(p, name); //對改值進行校驗,1該value必須是合法字符,2:該相對路徑必須包含相應的佔位符 //而後將改參數的全部註解進行處理,最終調用ParameterHandler.Path進行處理。 Converter<?, String> converter = retrofit.stringConverter(type, annotations); return new ParameterHandler.Path<>(name, converter, path.encoded()); } else if (annotation instanceof Query) { //Query註解,看不太懂,最後也是調用ParameterHandler.Query進行處理 Query query = (Query) annotation; String name = query.value(); boolean encoded = query.encoded(); Class<?> rawParameterType = Utils.getRawType(type); gotQuery = true; if (Iterable.class.isAssignableFrom(rawParameterType)) { if (!(type instanceof ParameterizedType)) { throw parameterError(p, rawParameterType.getSimpleName() + " must include generic type (e.g., " + rawParameterType.getSimpleName() + "<String>)"); } ParameterizedType parameterizedType = (ParameterizedType) type; Type iterableType = Utils.getParameterUpperBound(0, parameterizedType); Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations); return new ParameterHandler.Query<>(name, converter, encoded).iterable(); } else if (rawParameterType.isArray()) { Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType()); Converter<?, String> converter = retrofit.stringConverter(arrayComponentType, annotations); return new ParameterHandler.Query<>(name, converter, encoded).array(); } else { Converter<?, String> converter = retrofit.stringConverter(type, annotations); return new ParameterHandler.Query<>(name, converter, encoded); } } else if (annotation instanceof QueryMap) { Class<?> rawParameterType = Utils.getRawType(type); if (!Map.class.isAssignableFrom(rawParameterType)) { throw parameterError(p, "@QueryMap parameter type must be Map."); } Type mapType = Utils.getSupertype(type, rawParameterType, Map.class); if (!(mapType instanceof ParameterizedType)) { throw parameterError(p, "Map must include generic types (e.g., Map<String, String>)"); } ParameterizedType parameterizedType = (ParameterizedType) mapType; Type keyType = Utils.getParameterUpperBound(0, parameterizedType); if (String.class != keyType) { throw parameterError(p, "@QueryMap keys must be of type String: " + keyType); } Type valueType = Utils.getParameterUpperBound(1, parameterizedType); Converter<?, String> valueConverter = retrofit.stringConverter(valueType, annotations); return new ParameterHandler.QueryMap<>(valueConverter, ((QueryMap) annotation).encoded()); } else if (annotation instanceof Header) { Header header = (Header) annotation; String name = header.value(); Class<?> rawParameterType = Utils.getRawType(type); if (Iterable.class.isAssignableFrom(rawParameterType)) { if (!(type instanceof ParameterizedType)) { throw parameterError(p, rawParameterType.getSimpleName() + " must include generic type (e.g., " + rawParameterType.getSimpleName() + "<String>)"); } ParameterizedType parameterizedType = (ParameterizedType) type; Type iterableType = Utils.getParameterUpperBound(0, parameterizedType); Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations); return new ParameterHandler.Header<>(name, converter).iterable(); } else if (rawParameterType.isArray()) { Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType()); Converter<?, String> converter = retrofit.stringConverter(arrayComponentType, annotations); return new ParameterHandler.Header<>(name, converter).array(); } else { Converter<?, String> converter = retrofit.stringConverter(type, annotations); return new ParameterHandler.Header<>(name, converter); } } else if (annotation instanceof HeaderMap) { Class<?> rawParameterType = Utils.getRawType(type); if (!Map.class.isAssignableFrom(rawParameterType)) { throw parameterError(p, "@HeaderMap parameter type must be Map."); } Type mapType = Utils.getSupertype(type, rawParameterType, Map.class); if (!(mapType instanceof ParameterizedType)) { throw parameterError(p, "Map must include generic types (e.g., Map<String, String>)"); } ParameterizedType parameterizedType = (ParameterizedType) mapType; Type keyType = Utils.getParameterUpperBound(0, parameterizedType); if (String.class != keyType) { throw parameterError(p, "@HeaderMap keys must be of type String: " + keyType); } Type valueType = Utils.getParameterUpperBound(1, parameterizedType); Converter<?, String> valueConverter = retrofit.stringConverter(valueType, annotations); return new ParameterHandler.HeaderMap<>(valueConverter); } else if (annotation instanceof Field) { if (!isFormEncoded) { throw parameterError(p, "@Field parameters can only be used with form encoding."); } Field field = (Field) annotation; String name = field.value(); boolean encoded = field.encoded(); gotField = true; Class<?> rawParameterType = Utils.getRawType(type); if (Iterable.class.isAssignableFrom(rawParameterType)) { if (!(type instanceof ParameterizedType)) { throw parameterError(p, rawParameterType.getSimpleName() + " must include generic type (e.g., " + rawParameterType.getSimpleName() + "<String>)"); } ParameterizedType parameterizedType = (ParameterizedType) type; Type iterableType = Utils.getParameterUpperBound(0, parameterizedType); Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations); return new ParameterHandler.Field<>(name, converter, encoded).iterable(); } else if (rawParameterType.isArray()) { Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType()); Converter<?, String> converter = retrofit.stringConverter(arrayComponentType, annotations); return new ParameterHandler.Field<>(name, converter, encoded).array(); } else { Converter<?, String> converter = retrofit.stringConverter(type, annotations); return new ParameterHandler.Field<>(name, converter, encoded); } } else if (annotation instanceof FieldMap) { if (!isFormEncoded) { throw parameterError(p, "@FieldMap parameters can only be used with form encoding."); } Class<?> rawParameterType = Utils.getRawType(type); if (!Map.class.isAssignableFrom(rawParameterType)) { throw parameterError(p, "@FieldMap parameter type must be Map."); } Type mapType = Utils.getSupertype(type, rawParameterType, Map.class); if (!(mapType instanceof ParameterizedType)) { throw parameterError(p, "Map must include generic types (e.g., Map<String, String>)"); } ParameterizedType parameterizedType = (ParameterizedType) mapType; Type keyType = Utils.getParameterUpperBound(0, parameterizedType); if (String.class != keyType) { throw parameterError(p, "@FieldMap keys must be of type String: " + keyType); } Type valueType = Utils.getParameterUpperBound(1, parameterizedType); Converter<?, String> valueConverter = retrofit.stringConverter(valueType, annotations); gotField = true; return new ParameterHandler.FieldMap<>(valueConverter, ((FieldMap) annotation).encoded()); } else if (annotation instanceof Part) { if (!isMultipart) { throw parameterError(p, "@Part parameters can only be used with multipart encoding."); } Part part = (Part) annotation; gotPart = true; String partName = part.value(); Class<?> rawParameterType = Utils.getRawType(type); if (partName.isEmpty()) { if (Iterable.class.isAssignableFrom(rawParameterType)) { if (!(type instanceof ParameterizedType)) { throw parameterError(p, rawParameterType.getSimpleName() + " must include generic type (e.g., " + rawParameterType.getSimpleName() + "<String>)"); } ParameterizedType parameterizedType = (ParameterizedType) type; Type iterableType = Utils.getParameterUpperBound(0, parameterizedType); if (!MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(iterableType))) { throw parameterError(p, "@Part annotation must supply a name or use MultipartBody.Part parameter type."); } return ParameterHandler.RawPart.INSTANCE.iterable(); } else if (rawParameterType.isArray()) { Class<?> arrayComponentType = rawParameterType.getComponentType(); if (!MultipartBody.Part.class.isAssignableFrom(arrayComponentType)) { throw parameterError(p, "@Part annotation must supply a name or use MultipartBody.Part parameter type."); } return ParameterHandler.RawPart.INSTANCE.array(); } else if (MultipartBody.Part.class.isAssignableFrom(rawParameterType)) { return ParameterHandler.RawPart.INSTANCE; } else { throw parameterError(p, "@Part annotation must supply a name or use MultipartBody.Part parameter type."); } } else { Headers headers = Headers.of("Content-Disposition", "form-data; name=\"" + partName + "\"", "Content-Transfer-Encoding", part.encoding()); if (Iterable.class.isAssignableFrom(rawParameterType)) { if (!(type instanceof ParameterizedType)) { throw parameterError(p, rawParameterType.getSimpleName() + " must include generic type (e.g., " + rawParameterType.getSimpleName() + "<String>)"); } ParameterizedType parameterizedType = (ParameterizedType) type; Type iterableType = Utils.getParameterUpperBound(0, parameterizedType); if (MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(iterableType))) { throw parameterError(p, "@Part parameters using the MultipartBody.Part must not " + "include a part name in the annotation."); } Converter<?, RequestBody> converter = retrofit.requestBodyConverter(iterableType, annotations, methodAnnotations); return new ParameterHandler.Part<>(headers, converter).iterable(); } else if (rawParameterType.isArray()) { Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType()); if (MultipartBody.Part.class.isAssignableFrom(arrayComponentType)) { throw parameterError(p, "@Part parameters using the MultipartBody.Part must not " + "include a part name in the annotation."); } Converter<?, RequestBody> converter = retrofit.requestBodyConverter(arrayComponentType, annotations, methodAnnotations); return new ParameterHandler.Part<>(headers, converter).array(); } else if (MultipartBody.Part.class.isAssignableFrom(rawParameterType)) { throw parameterError(p, "@Part parameters using the MultipartBody.Part must not " + "include a part name in the annotation."); } else { Converter<?, RequestBody> converter = retrofit.requestBodyConverter(type, annotations, methodAnnotations); return new ParameterHandler.Part<>(headers, converter); } } } else if (annotation instanceof PartMap) { if (!isMultipart) { throw parameterError(p, "@PartMap parameters can only be used with multipart encoding."); } gotPart = true; Class<?> rawParameterType = Utils.getRawType(type); if (!Map.class.isAssignableFrom(rawParameterType)) { throw parameterError(p, "@PartMap parameter type must be Map."); } Type mapType = Utils.getSupertype(type, rawParameterType, Map.class); if (!(mapType instanceof ParameterizedType)) { throw parameterError(p, "Map must include generic types (e.g., Map<String, String>)"); } ParameterizedType parameterizedType = (ParameterizedType) mapType; Type keyType = Utils.getParameterUpperBound(0, parameterizedType); if (String.class != keyType) { throw parameterError(p, "@PartMap keys must be of type String: " + keyType); } Type valueType = Utils.getParameterUpperBound(1, parameterizedType); if (MultipartBody.Part.class.isAssignableFrom(Utils.getRawType(valueType))) { throw parameterError(p, "@PartMap values cannot be MultipartBody.Part. " + "Use @Part List<Part> or a different value type instead."); } Converter<?, RequestBody> valueConverter = retrofit.requestBodyConverter(valueType, annotations, methodAnnotations); PartMap partMap = (PartMap) annotation; return new ParameterHandler.PartMap<>(valueConverter, partMap.encoding()); } else if (annotation instanceof Body) { if (isFormEncoded || isMultipart) { throw parameterError(p, "@Body parameters cannot be used with form or multi-part encoding."); } if (gotBody) { throw parameterError(p, "Multiple @Body method annotations found."); } Converter<?, RequestBody> converter; try { converter = retrofit.requestBodyConverter(type, annotations, methodAnnotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw parameterError(e, p, "Unable to create @Body converter for %s", type); } gotBody = true; return new ParameterHandler.Body<>(converter); } return null; // Not a Retrofit annotation.找不到該註解 }
從上面能夠看出,改立參數註解的套路就是:先判斷該註解的類型,而後使用策略模式分別調用ParameterHandler裏對應的子類來處理
寫到這裏我已經暈了。暈暈乎乎好舒服
有時候咱們須要動態的設置請求header中的某個請求頭的值,這個時候就可使用@Header來修飾個參數。
最終都是講header裏的信息提取到Request裏面
static final class Header<T> extends ParameterHandler<T> { private final String name; private final Converter<T, String> valueConverter; Header(String name, Converter<T, String> valueConverter) { this.name = checkNotNull(name, "name == null"); this.valueConverter = valueConverter; } @Override void apply(RequestBuilder builder, T value) throws IOException { if (value == null) return; // Skip null values. builder.addHeader(name, valueConverter.convert(value)); } }
void addHeader(String name, String value) { if ("Content-Type".equalsIgnoreCase(name)) { MediaType type = MediaType.parse(value); if (type == null) { throw new IllegalArgumentException("Malformed content type: " + value); } contentType = type; } else { requestBuilder.addHeader(name, value); } }
調用requestBuilder.addHeader()方法。
這個requestBuilder是OKHttp中Request的內部靜態類Builder類的一個對象。
private final Request.Builder requestBuilder;
從中咱們能夠看出最後將@Header註釋的參數的值解析後添加到Request對象中的Header信息裏。
有時候請求路徑是不定的,即請求路徑裏的某個segment是變化的,也就是須要咱們使用參數來動態的改變,這個時候咱們就須要使用@Path 來修飾這個參數
static final class Path<T> extends ParameterHandler<T> { private final String name; //參數名,佔位符 private final Converter<T, String> valueConverter; private final boolean encoded; //是否編碼 Path(String name, Converter<T, String> valueConverter, boolean encoded) { this.name = checkNotNull(name, "name == null"); this.valueConverter = valueConverter; this.encoded = encoded; } @Override void apply(RequestBuilder builder, T value) throws IOException { if (value == null) { throw new IllegalArgumentException( "Path parameter \"" + name + "\" value must not be null."); } builder.addPathParam(name, valueConverter.convert(value), encoded); } }
void addPathParam(String name, String value, boolean encoded) { if (relativeUrl == null) { // The relative URL is cleared when the first query parameter is set. throw new AssertionError(); } //將佔位符」{name}」使用value替換 relativeUrl = relativeUrl.replace("{" + name + "}", canonicalizeForPath(value, encoded)); }
@Query用來修飾接口方法中的查詢字段
static final class Query<T> extends ParameterHandler<T> { private final String name; private final Converter<T, String> valueConverter; private final boolean encoded; Query(String name, Converter<T, String> valueConverter, boolean encoded) { this.name = checkNotNull(name, "name == null"); this.valueConverter = valueConverter; this.encoded = encoded; } @Override void apply(RequestBuilder builder, T value) throws IOException { if (value == null) return; // Skip null values. builder.addQueryParam(name, valueConverter.convert(value), encoded); } }
//將查詢參數組合到相對路徑上。 void addQueryParam(String name, String value, boolean encoded) { if (relativeUrl != null) { // Do a one-time combination of the built relative URL and the base URL. urlBuilder = baseUrl.newBuilder(relativeUrl); if (urlBuilder == null) { throw new IllegalArgumentException( "Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl); } relativeUrl = null; } if (encoded) { urlBuilder.addEncodedQueryParameter(name, value); } else { urlBuilder.addQueryParameter(name, value); } }
當接口中的一個 方法有比較多的查詢字段時,所有定義到方法中時比較麻煩且容易出錯,這個使用咱們徹底能夠將全部的查詢參數放到一個Map裏面。
可想而知,其內部實現一定是遍歷map ,而後像處理@Query參數同樣調用addQueryParam()處理每一個查詢參數。
static final class FieldMap<T> extends ParameterHandler<Map<String, T>> { private final Converter<T, String> valueConverter; private final boolean encoded; FieldMap(Converter<T, String> valueConverter, boolean encoded) { this.valueConverter = valueConverter; this.encoded = encoded; } @Override void apply(RequestBuilder builder, Map<String, T> value) throws IOException { if (value == null) { throw new IllegalArgumentException("Field map was null."); } for (Map.Entry<String, T> entry : value.entrySet()) { String entryKey = entry.getKey(); if (entryKey == null) { throw new IllegalArgumentException("Field map contained null key."); } T entryValue = entry.getValue(); if (entryValue == null) { throw new IllegalArgumentException( "Field map contained null value for key '" + entryKey + "'."); } //果真不假 builder.addFormField(entryKey, valueConverter.convert(entryValue), encoded); } } }
@Field註解通常用在表單參數的提交上
static final class Field<T> extends ParameterHandler<T> { private final String name; //參數名字 private final Converter<T, String> valueConverter; //參數值轉換器 private final boolean encoded; //是否編碼 Field(String name, Converter<T, String> valueConverter, boolean encoded) { this.name = checkNotNull(name, "name == null"); this.valueConverter = valueConverter; this.encoded = encoded; } @Override void apply(RequestBuilder builder, T value) throws IOException { if (value == null) return; // Skip null values. 因此使用@Field修飾的字段,是不會上傳到服務器的。 //調用ResuestBuilder對象的具體想法來處理@Field修飾的表單字段 builder.addFormField(name, valueConverter.convert(value), encoded); } }
void addFormField(String name, String value, boolean encoded) { //根據參數值是否被編碼,調用不一樣的方法。formBuilder是OKHttp中的一個類。也是使用Builder模式建立的。 if (encoded) { formBuilder.addEncoded(name, value); } else { formBuilder.add(name, value); } }
@FieldMap
假如表單參數有不少個,咱們可使用一個Map<String,String>來表示,而後使用@FieldMap註解來修飾該參數就好了。可想而知,如同@QueryMap同樣,其內部實現確定是遍歷Map,而後像處理@Field參數同樣調用
builder.addFormField(name, valueConverter.convert(value), encoded);
在如下須要提交表單的請求裏,咱們可使用@Field,@FieldMap,咱們還可使用@Body來修飾咱們提交的表單數據,這個時候咱們須要定義一個Bean類,Bean類的各個Field必須和表單字段的key同樣
static final class Body<T> extends ParameterHandler<T> { private final Converter<T, RequestBody> converter; Body(Converter<T, RequestBody> converter) { this.converter = converter; } @Override void apply(RequestBuilder builder, T value) { if (value == null) { throw new IllegalArgumentException("Body parameter value must not be null."); } RequestBody body; try { body = converter.convert(value); } catch (IOException e) { throw new RuntimeException("Unable to convert " + value + " to RequestBody", e); } builder.setBody(body); } }
這裏Retrofit並無像@Field同樣處理表單參數。仔細想一想也對,由於凡是提交的表單數據都須要放到請求體裏面,即便使用@Field,@FieldMap提交的數據,最終仍是須要放到請求體裏面。
以上三個註解都是使用修飾上傳文件的參數的,
從對上面的分析能夠知道,咱們在提取使用註解修飾的參數後將值存放到RequestBuilder對象裏。
這裏又引入了RequestBuilder類
final class RequestBuilder { private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; private static final String PATH_SEGMENT_ALWAYS_ENCODE_SET = " \"<>^`{}|\\?#"; private final String method; //方法類型 private final HttpUrl baseUrl; //scheme+host private String relativeUrl; //相對路徑 private HttpUrl.Builder urlBuilder; //URL構造器 private final Request.Builder requestBuilder; //OkHttp中Request構造器 private MediaType contentType; //提交表單的數據類型 private final boolean hasBody; //是否有請求體 private MultipartBody.Builder multipartBuilder; //上傳文件的構造器 private FormBody.Builder formBuilder; //表單數據的構造器 private RequestBody body; //請求體 RequestBuilder(String method, HttpUrl baseUrl, String relativeUrl, Headers headers, MediaType contentType, boolean hasBody, boolean isFormEncoded, boolean isMultipart) { this.method = method; this.baseUrl = baseUrl; this.relativeUrl = relativeUrl; this.requestBuilder = new Request.Builder(); this.contentType = contentType; this.hasBody = hasBody; if (headers != null) { requestBuilder.headers(headers); } if (isFormEncoded) { // Will be set to 'body' in 'build'. formBuilder = new FormBody.Builder(); } else if (isMultipart) { // Will be set to 'body' in 'build'. multipartBuilder = new MultipartBody.Builder(); multipartBuilder.setType(MultipartBody.FORM); } } void setRelativeUrl(Object relativeUrl) { if (relativeUrl == null) throw new NullPointerException("@Url parameter is null."); this.relativeUrl = relativeUrl.toString(); } void addHeader(String name, String value) { if ("Content-Type".equalsIgnoreCase(name)) { MediaType type = MediaType.parse(value); if (type == null) { throw new IllegalArgumentException("Malformed content type: " + value); } contentType = type; } else { requestBuilder.addHeader(name, value); } } void addPathParam(String name, String value, boolean encoded) { if (relativeUrl == null) { // The relative URL is cleared when the first query parameter is set. throw new AssertionError(); } relativeUrl = relativeUrl.replace("{" + name + "}", canonicalizeForPath(value, encoded)); } private static String canonicalizeForPath(String input, boolean alreadyEncoded) { int codePoint; for (int i = 0, limit = input.length(); i < limit; i += Character.charCount(codePoint)) { codePoint = input.codePointAt(i); if (codePoint < 0x20 || codePoint >= 0x7f || PATH_SEGMENT_ALWAYS_ENCODE_SET.indexOf(codePoint) != -1 || (!alreadyEncoded && (codePoint == '/' || codePoint == '%'))) { // Slow path: the character at i requires encoding! Buffer out = new Buffer(); out.writeUtf8(input, 0, i); canonicalizeForPath(out, input, i, limit, alreadyEncoded); return out.readUtf8(); } } // Fast path: no characters required encoding. return input; } private static void canonicalizeForPath(Buffer out, String input, int pos, int limit, boolean alreadyEncoded) { Buffer utf8Buffer = null; // Lazily allocated. int codePoint; for (int i = pos; i < limit; i += Character.charCount(codePoint)) { codePoint = input.codePointAt(i); if (alreadyEncoded && (codePoint == '\t' || codePoint == '\n' || codePoint == '\f' || codePoint == '\r')) { // Skip this character. } else if (codePoint < 0x20 || codePoint >= 0x7f || PATH_SEGMENT_ALWAYS_ENCODE_SET.indexOf(codePoint) != -1 || (!alreadyEncoded && (codePoint == '/' || codePoint == '%'))) { // Percent encode this character. if (utf8Buffer == null) { utf8Buffer = new Buffer(); } utf8Buffer.writeUtf8CodePoint(codePoint); while (!utf8Buffer.exhausted()) { int b = utf8Buffer.readByte() & 0xff; out.writeByte('%'); out.writeByte(HEX_DIGITS[(b >> 4) & 0xf]); out.writeByte(HEX_DIGITS[b & 0xf]); } } else { // This character doesn't need encoding. Just copy it over. out.writeUtf8CodePoint(codePoint); } } } void addQueryParam(String name, String value, boolean encoded) { if (relativeUrl != null) { // Do a one-time combination of the built relative URL and the base URL. urlBuilder = baseUrl.newBuilder(relativeUrl); if (urlBuilder == null) { throw new IllegalArgumentException( "Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl); } relativeUrl = null; } if (encoded) { urlBuilder.addEncodedQueryParameter(name, value); } else { urlBuilder.addQueryParameter(name, value); } } void addFormField(String name, String value, boolean encoded) { if (encoded) { formBuilder.addEncoded(name, value); } else { formBuilder.add(name, value); } } void addPart(Headers headers, RequestBody body) { multipartBuilder.addPart(headers, body); } void addPart(MultipartBody.Part part) { multipartBuilder.addPart(part); } void setBody(RequestBody body) { this.body = body; } Request build() { HttpUrl url; HttpUrl.Builder urlBuilder = this.urlBuilder; if (urlBuilder != null) { url = urlBuilder.build(); } else { // No query parameters triggered builder creation, just combine the relative URL and base URL. url = baseUrl.resolve(relativeUrl); if (url == null) { throw new IllegalArgumentException( "Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl); } } RequestBody body = this.body; if (body == null) { // Try to pull from one of the builders. if (formBuilder != null) { body = formBuilder.build(); } else if (multipartBuilder != null) { body = multipartBuilder.build(); } else if (hasBody) { // Body is absent, make an empty body. body = RequestBody.create(null, new byte[0]); } } MediaType contentType = this.contentType; if (contentType != null) { if (body != null) { body = new ContentTypeOverridingRequestBody(body, contentType); } else { requestBuilder.addHeader("Content-Type", contentType.toString()); } } //生成一個Request對象 return requestBuilder .url(url) .method(method, body) .build(); } private static class ContentTypeOverridingRequestBody extends RequestBody { private final RequestBody delegate; private final MediaType contentType; ContentTypeOverridingRequestBody(RequestBody delegate, MediaType contentType) { this.delegate = delegate; this.contentType = contentType; } @Override public MediaType contentType() { return contentType; } @Override public long contentLength() throws IOException { return delegate.contentLength(); } @Override public void writeTo(BufferedSink sink) throws IOException { delegate.writeTo(sink); } } }
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
在建立了ServiceMethod對象後,使用該ServiceMethod對象和其參數建立一個OKHttPCall對象
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
在合適的時候調用ServiceMethod對象的toRequest方法生成一個Request對象,toReques()的內部實現就是調用RequestBuilder對象的build方法。
/** Builds an HTTP request from method arguments. */ Request toRequest(Object... args) throws IOException { RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers, contentType, hasBody, isFormEncoded, isMultipart); @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types. ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers; int argumentCount = args != null ? args.length : 0; if (argumentCount != handlers.length) { throw new IllegalArgumentException("Argument count (" + argumentCount + ") doesn't match expected count (" + handlers.length + ")"); } for (int p = 0; p < argumentCount; p++) { handlers[p].apply(requestBuilder, args[p]); } return requestBuilder.build(); }
OkHttpCall 實現了Call接口,這個Call接口和OkHttp中的Call接口同樣,畢竟一家公司嘛。
其實就是對OkHttpCall 作了一層包裝。
最後方法的執行時經過調用
return serviceMethod.callAdapter.adapt(okHttpCall);
返回接口中方法定義的返回值。
這塊的流程就是構造一個OKHttp對象須要使用ServiceMethod對象和相應的參數。
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
最後建立具體的Call對象時
private okhttp3.Call createRawCall() throws IOException { Request request = serviceMethod.toRequest(args); okhttp3.Call call = serviceMethod.callFactory.newCall(request); if (call == null) { throw new NullPointerException("Call.Factory returned null."); } return call; }
調用了ServiceMethod對象的toRequest方法,而後使用這個request對象建立了一個Call對象。
/** Builds an HTTP request from method arguments. */ Request toRequest(Object... args) throws IOException { RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers, contentType, hasBody, isFormEncoded, isMultipart); @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types. ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers; int argumentCount = args != null ? args.length : 0; if (argumentCount != handlers.length) { throw new IllegalArgumentException("Argument count (" + argumentCount + ") doesn't match expected count (" + handlers.length + ")"); } for (int p = 0; p < argumentCount; p++) { handlers[p].apply(requestBuilder, args[p]); } //生成一個Request對象 return requestBuilder.build(); }