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