整體Retrofit內容如下:
本片文章的主要內容如下:
- 1、整體流程簡介
- 2、流程詳解
- 3、總結
- 4、okHttp+Retrofit的整體架構
一、Retrofit整體流程簡介
其實整個Retrofit的流程如下圖:
PS:該圖不是我畫的,網上盜來的
- 這張圖完美的詮釋了Retrofit的整個流程圖
- 首選創建Retrofit,配置響應的參數,然后Retrofit會的的請求都會交給OkHttp去執行,執行的結果返回Response,再根據轉換器進行解析成相對應的返回值類型T。Retrofit內部使用了動態代理,方便了使用,通過retrofit.create返回的其實是一個動態代理類,所有具體的邏輯處理交給ServiceMethod來進行處理。
如果拋各種開適配器和Converter的情況,只考慮發起請求,我們可以這樣理解的流程如下圖:
Retrofit總體使用的是外觀模式,Retrofit持有所有子系統的引用;Retrofit有兩個比較重要的兩個Factory,一個是用來生成對應"Call"的CallAdapter的CallAdapterFactory;一個是用來進行響應數據轉化(反序列化)的ConvertFactory;這兩個都可以用戶自己進行添加。在自定義的Service中,每一個method一一對應一個ServiceMethod,而ServiceMethod持有一個Retrofit,前面兩個Factory以及生成的Request的RequestBuilder;在okHttp中,Request需要自己進行定義創建,而Retrofit簡化了這個操作,進行相應的封裝,使用注解的方式來定義RequestBuilder相關參數信息;注解信息的解析則在parseMethodAnnotation()方法里面和parseParameterAnnotation()方法里面分別解析方法注解和參數注解。最終通過RequestBuilder來具體陳生一個Request,RequestBuilder中持有okHttp中的Request.Builder類的引用,其創建Request的過程其實都是交給okHttp來操作的;生成的Request最終封裝成一個OkHttpCall,OkHttpCall則可以看做是對okHttp中Call的代理,同時對okHttp的返回Response進行解析,使用convertFactory將其解析為用戶所期望的返回類型。
二、流程詳解
我們講解Retrofit整體流程,就依據官方給的demo來吧,代碼如下:
代碼如下:
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(
@Path("owner") String owner,
@Path("repo") String repo);
}
public static void main(String... args) throws IOException {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHub github = retrofit.create(GitHub.class);
Call<List<Contributor>> call = github.contributors("square", "retrofit");
List<Contributor> contributors = call.execute().body();
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}
}
這就是Retrofit最簡單的用法,可以從代碼看出:
- 首先定義一個接口,接口中的方法用注解方式聲明了HTTP請求的相關參數,包括GET方法,相關參數等。方法的返回值為Call<List<Contributor>>,其中Contributor是定義的一個JavaBean類,即業務所需要的數據格式。
- 其次實例化了一個Retrofit對象,用Retrofit的builder,指定了baseUrl,指定了ConverterFactory,即表示用Gson解析返回值來得到JavaBean。
- 用retrofit.create(GitHub.class)方法得到了一個GitHub實例對象(框架動態代理的方法幫我們生成了接口的實例,后續詳細說)
(3).用retrofit.create(GitHub.class)方法得到了GitHub實例對象(框架用動態代理的方式幫我們生成了接口的實例,后續詳細說),調用對象方法得到call對象。其中,call對象有excute()和enqueue()方法,分別為同步和異步進行網絡請求。
所以我們把整個流程分解為5大部分
- 1、Retrofit初始化
- 2、發起網絡請求
- 3、執行請求
- 4、處理響應
(一)、Retrofit初始化
具體代碼如下:
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
Retrofit的構建,其實就是一個簡單的建造者模式(Builder),通過Builder來進行構建。如下圖所示:
其中Retrofit.Builder有以下參數可以設置:
- okhttp3.Call.Factory callFactory。
- HttpUrl baseUrl。
- List<Converter.Factory> converterFactories。
- List<CallAdapter.Factory> adapterFactories。
- Executor callbackExecutor。
- boolean validateEagerly。
補充說明:
- 由于是調用無參的Retrofit.Builder()的構造函數,而無參的構造函數內部又調用了 this(Platform.get());在 this(Platform.get())里面有調用了 converterFactories.add(new BuiltInConverters()),所以Builder的里面默認帶一個BuiltInConverters的實例。
- 以上參數均可以通過Retrofit.Builder的addXXX方法進行添加,如果大家對上面的變量理解有疑問,請看上篇文章。
下面我們重點說下build()方法的內容:
代碼如下;
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
// 創建OkHttp,目前Retrofit只支持OkHttp
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
// 創建Executor
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// 對adapters進行保護性拷貝,并且加入默認的call adapter(使用上面創建的Executor來構建,可以認為是把主線程中的Handler傳入, 構建adapterFactories集合,將defaultAdapterFactory加入其中
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// 對converters進行保護性拷貝,一般傳入的為GsonConverterFactory對象,其作用主要是將json轉換成java對象
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
//構建Retrofit對象
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
build方法主要思路:
- 1、創建callFactory對象
- 2、創建callbackExecutor對象,有平臺可知,其實就是主線程,通過MainLooper來構建Handler,其execute方法本質是Handler.post(runnable),主要用于線程切換
- 3、創建adapterFactories集合,同時添加一個默認的adapterFactory,即通過調用platform.defaultCallAdapterFactory(callbackExecutor),其實就是ExecutorCallAdapterFactory類實例,這樣保證adapterFactories里面至少有一個adapterFactory。而ExecutorCallAdapterFactory的get(Type,Annotation[], Retrofit)方法里面返回的CallAdapter其實是一個new的一個匿名內部類,而這個匿名內部類的里面的adapt(Call<Object>)方法返回的是一個ExecutorCallbackCall對象,而ExecutorCallbackCall對象內部又持有retrofit2.Call對象,如果大家之前看過我前面寫的文章知道,retrofit2.Call僅僅是接口,retrofit2.Call的實現類是OkHttpCall,而OkHttpCall里面又吃有一個okhttp3.Call,真正發起請求的就是這個okhttp3.Call,這樣ExecutorCallbackCall就間接持有了okhttp3.Call,其實OkHttpCall就是okhttp3.Call的包裝類。
- 4、創建一個converterFactories的集合,并把自己的converterFactories添加進去,這個converterFactories在調用無參的構造函數時進行初始化的。
PS:
ExecutorCallbackCall類里面有個變量叫做final retrofit2.Call<T> delegate,而這個retrofit2.Call的具體實現類是retrofit2.OkHttpCall,在OkHttpCall類內部有一個變量就叫做 private okhttp3.Call rawCall;這樣ExecutorCallbackCall就間接持有一個okhttp3.Call的對象了,如下圖:
(二)、發起網絡請求
1、定義Api接口:
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(@Path("owner") String owner, @Path("repo") String repo);
關于兩個注解:@GET 和 @Path,我這里就不詳細說明了,不懂的看我前面的文章。
2、獲取一個請求操作:
GitHub github = retrofit.create(GitHub.class);
調用retrofit.create(GitHubService.class)我們就可以獲取一個實例了
這里面我們詳細分析下:
3、retrofit.create()方法詳解:
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 (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
流程圖如下:
(1)說下整體流程成,運用動態代理技術獲取了一個GitHubService的一個實例。
這里面運用了動態代理技術,動態代理方法Proxy.newProxyInstance返回的便是該接口的代理對象。invoke方法為接口方法的具體實現,invoke()方法里面的method為具體的方法(在demo中為contributors方法);args是該方法的參數(在demo中為new String[]{"square", "retrofit"})。
動態代理模型如下:
如果大家對這塊技術不是很熟悉請看我上一篇文章。
(2)說下eagerlyValidateMethods()方法
大家看下這塊代碼:
if (validateEagerly) {
eagerlyValidateMethods(service);
}
private void eagerlyValidateMethods(Class<?> service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method)) {
loadServiceMethod(method);
}
}
}
所以如果我們在Retrofit.Builder的validateEagerly參數被設置為true,我們在create方法執行的時候,就會遍歷這個service的所有方法,由于platform.isDefaultMethod(method)在Android平臺默認返回false,所以一定會執行 loadServiceMethod(method),也就是說如果設置validateEagerly=true,則Retrofit會把這個service接口的所有方法都load進來,也就說一次性全部導入。
(3)說下loadServiceMethod()方法
ServiceMethod<?, ?> loadServiceMethod(Method method) {
ServiceMethod<?, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
- serviceMethodCache是一個一級緩存。使用緩存的目的是,由于反射比較消耗性能,所以為了避免這種性能消耗,我們保證同一個接口(api)的同一個方法,只會創建一次,由于我們每次獲取的接口(api)實例都是傳入的class對象,而class對象是在進程內單例的,所以獲取她的同一個方法Method實例也是單利的,所以這個緩存是有效的。
- loadServiceMethod()方法,通過代碼我們知道,結合了一級緩存serviceMethodCache,如果緩存里面有,則直接從緩存里面拿,如果緩存沒有,則new一個ServiceMethod.Builder對象,然后調用它的build()方法來獲取一個ServiceMethod,把它放入serviceMethodCache中,并返回。
(4)、new ServiceMethod.Builder<>(this, method)方法詳解:
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
通過這個ServiceMethod.Builder的構造函數, ServiceMethod.Builder這個實例就獲取了Retrofit對象和method對象,通過對method的解析,又獲取了方法的注解,該方法參數的類型,該方法參數的注解。完成了ServiceMethod.Builder的初始化。
(5)、ServiceMethod.Builder<>().build()方法詳解:
public ServiceMethod build() {
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();
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
if (httpMethod == null) {
throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
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).");
}
}
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);
}
if (relativeUrl == null && !gotUrl) {
throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
}
if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
throw methodError("Non-body HTTP method cannot contain @Body.");
}
if (isFormEncoded && !gotField) {
throw methodError("Form-encoded method must contain at least one @Field.");
}
if (isMultipart && !gotPart) {
throw methodError("Multipart method must contain at least one @Part.");
}
return new ServiceMethod<>(this);
}
這個方法里面我們重點講解下幾個部分
第一部分-----方法createCallAdapter()解析
createCallAdapter主要是獲取對應的CallAdapter的對象,那我們就詳細分析下createCallAdapter()內部的代碼。
看下代碼
private CallAdapter<T, R> createCallAdapter() {
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError("Service methods cannot return void.");
}
Annotation[] annotations = method.getAnnotations();
try {
return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) {
throw methodError(e, "Unable to create call adapter for %s", returnType);
}
}
callAdapter 主要負責把retrofit2.Call<R> 轉化為T (注意和okhttp3.Call 區分開來) ,retrofit2.Call<R> 表示的是對一個Retrofit的接口方法的調用,也就是我們舉得例子 Call<List<Contributor>> call = github.contributors("square", "retrofit")。通過上述代碼我們知道createCallAdapter的內部其實是通過retrofit類中的callAdapter()方法來實現的。那我們再來看下retrofit類中的callAdapter()方法:
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?, ?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
checkNotNull(returnType, "returnType == null");
checkNotNull(annotations, "annotations == null");
int start = adapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = adapterFactories.size(); i < count; i++) {
CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
StringBuilder builder = new StringBuilder("Could not locate call adapter for ")
.append(returnType)
.append(".\n");
if (skipPast != null) {
builder.append(" Skipped:");
for (int i = 0; i < start; i++) {
builder.append("\n * ").append(adapterFactories.get(i).getClass().getName());
}
builder.append('\n');
}
builder.append(" Tried:");
for (int i = start, count = adapterFactories.size(); i < count; i++) {
builder.append("\n * ").append(adapterFactories.get(i).getClass().getName());
}
throw new IllegalArgumentException(builder.toString());
}
之前我們講過,如果我們不addCallAdapterFactory()方法的話,那么在Retrofit.build()方法中,系統會添加一個平臺默認的值:
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
也就是沒有設置,那么這里應該會添加平臺默認的CallAdapterFactory,其實是ExecutorCallAdapterFactory類,。那我們再回到nextCallAdapter()中,在nextCallAdapter()方法中我們遍歷adapterFactories,調動每一個CallAdapter.Factory的get()方法來獲取一個CallAdapter。說白了,就是根據注解和返回值的類型,判斷,如果你這個CallAdapter支持則返回對應的CallAdapter,如果不支持則返回null。
第二部分-----createResponseConverter()方法解析
private Converter<ResponseBody, T> createResponseConverter() {
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.responseBodyConverter(responseType, annotations);
} catch (RuntimeException e) {
throw methodError(e, "Unable to create converter for %s", responseType);
}
}
Converter<ResponseBody, T>主要負責把okhttp3.ResponseBody轉化為類型T的對象。通過代碼我們知道其實是調用retrofit類中的responseBodyConverter()方法,那我們來看下responseBodyConverter()方法里面的具體實現
public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
return nextResponseBodyConverter(null, type, annotations);
}
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(Converter.Factory skipPast,
Type type, Annotation[] annotations) {
checkNotNull(type, "type == null");
checkNotNull(annotations, "annotations == null");
int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
return (Converter<ResponseBody, T>) converter;
}
}
StringBuilder builder = new StringBuilder("Could not locate ResponseBody converter for ")
.append(type)
.append(".\n");
if (skipPast != null) {
builder.append(" Skipped:");
for (int i = 0; i < start; i++) {
builder.append("\n * ").append(converterFactories.get(i).getClass().getName());
}
builder.append('\n');
}
builder.append(" Tried:");
for (int i = start, count = converterFactories.size(); i < count; i++) {
builder.append("\n * ").append(converterFactories.get(i).getClass().getName());
}
throw new IllegalArgumentException(builder.toString());
}
由于我們添加了
.addConverterFactory(GsonConverterFactory.create())
所以在Retrofit里面的converterFactories里面有兩個Converter.Factory,一個是默認的BuiltInConverters,一個是GsonConverterFactory。
那我們再回到nextResponseBodyConverter()中,在nextResponseBodyConverter()方法中我們遍歷converterFactories,調動每一個Converter.Factory的responseBodyConverter()方法來獲取一個Converter。說白了,就是根據返回值類型、參數注解、方法注解來獲取一個對應的Converter,如果你這個Converter.Factory支持則返回對應的Converter,則返回一個對應的Converter,如果不支持則返回null。在我們的demo中由于使用了GsonConverterFactory的get()方法返回的是GsonConverterFactory,而GsonConverterFactory的responseBodyConverter()返回到是GsonResponseBodyConverter實例。
第三部分-----方法注解解析
看代碼
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
通過遍歷該方法的所有方法注解,調用parseMethodAnnotation來解析方法注解,那我們就來看下里面的具體執行
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) {
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) {
if (isFormEncoded) {
throw methodError("Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
if (isMultipart) {
throw methodError("Only one encoding annotation is allowed.");
}
isFormEncoded = true;
}
}
- 1、通過解析方法的注解,我們來獲取HTTP的請求的請求方法,
- 2、然后調用parseHttpMethodAndPath()方法來設置Builder的relativeUrl和relativeUrlParamNames 兩個字段的值。
- 3、如果有設置請求頭,則獲取請求頭的值,并添加到Builder的headers 中
- 4、如果請求類型是Multipart的,則設置isMultipart =true
- 5、如果請求類型是isFormEncoded的,則設置 isFormEncoded=true
Multipart大家不了解的看我前面的文章,這里面說下isMultipart 和 isFormEncoded是互斥的,所以后面解析方法參數的時候,遇到一些錯誤的操作,會直接拋異常,比如你方法注解使用了@Multipart,但是在參數那里使用了注解Field,由于是互斥的,所以會拋異常。
所有通過方法注解解析(也就是parseMethodAnnotation()),我們完成了部分Builder的初始化。
第四部分-----內部檢驗
if (httpMethod == null) {
throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
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).");
}
}
- 1、請求方式一定要有
- 2、如果沒有請求體,則一定不能使用@Multipart和@FormUrlEncoded
第五部分-----參數注解解析
代碼如下:
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);
}
- 1、首先獲取注解的個數,由于注解的個數一定是和方法參數的個數一一對應的
- 2、給Buidler的parameterHandlers賦值,僅僅是new了一個對象,每個元素的值還是null
- 3、獲取入參的類型,并判斷,如果是不能處理的類型則直接拋異常
- 4、然后調用parseParameter來解析方法參數注解
那讓我們看下parseParameter()方法是如何進行解析方法參數的
private ParameterHandler<?> parseParameter(
int p, Type parameterType, Annotation[] annotations) {
ParameterHandler<?> result = null;
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;
}
if (result == null) {
throw parameterError(p, "No Retrofit annotation found.");
}
return result;
}
parseParameter方法內部其實也是遍歷這個參數的所有注解,然后通過調用
parseParameterAnnotation()方法來解析每一個注解。那么我們來看下
parseParameterAnnotation()方法內部的實現
private ParameterHandler<?> parseParameterAnnotation(
int p, Type type, Annotation[] annotations, Annotation annotation) {
if (annotation instanceof Url) {
if (gotUrl) {
throw parameterError(p, "Multiple @Url method annotations found.");
}
if (gotPath) {
throw parameterError(p, "@Path parameters may not be used with @Url.");
}
if (gotQuery) {
throw parameterError(p, "A @Url parameter must not come after a @Query");
}
if (relativeUrl != null) {
throw parameterError(p, "@Url cannot be used with @%s URL", httpMethod);
}
gotUrl = true;
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 {
throw parameterError(p,
"@Url must be okhttp3.HttpUrl, String, java.net.URI, or android.net.Uri type.");
}
} else if (annotation instanceof Path) {
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.");
}
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();
validatePathName(p, name);
Converter<?, String> converter = retrofit.stringConverter(type, annotations);
return new ParameterHandler.Path<>(name, converter, path.encoded());
} else if (annotation instanceof 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 QueryName) {
QueryName query = (QueryName) annotation;
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.QueryName<>(converter, encoded).iterable();
} else if (rawParameterType.isArray()) {
Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
Converter<?, String> converter =
retrofit.stringConverter(arrayComponentType, annotations);
return new ParameterHandler.QueryName<>(converter, encoded).array();
} else {
Converter<?, String> converter =
retrofit.stringConverter(type, annotations);
return new ParameterHandler.QueryName<>(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的靜態內部類對象,當然在生成不同的
ParameterHandler靜態內部類對象之前,有一定的驗證條件,保證內部邏輯清晰,不矛盾。
所有通過參數注解解析(也就是parseParameter()),我們完成了另一部分Builder的初始化,這樣整個Builder的初始化就完成了,他對應的各個屬性也賦值完畢。
第六部分-----排除錯誤邏輯
if (relativeUrl == null && !gotUrl) {
throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
}
if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
throw methodError("Non-body HTTP method cannot contain @Body.");
}
if (isFormEncoded && !gotField) {
throw methodError("Form-encoded method must contain at least one @Field.");
}
if (isMultipart && !gotPart) {
throw methodError("Multipart method must contain at least one @Part.");
}
主要是把互斥的幾種情況添加進去,如果出現則違反了原則,直接拋異常
第七部分-----構造ServiceMethod
return new ServiceMethod<>(this);
這時候我們用這個Builder作為參數new了一個ServiceMethod對象,并返回
ServiceMethod(Builder<R, 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里面的相應字段的值傳遞給ServiceMethod。 至此整個ServiceMethod.Builderd的build()方法執行完畢。
所以在ServiceMethod.Builder.build()方法里面主要是給Builder的各個屬性賦值。保證這些屬性最后正確地傳遞給ServiceMethod。在build()方法里面的流程如下:
- 1、獲取對應的CallAdatper
- 2、獲取對應的Converter
- 3、方法注解解析,給相應的ServiceMethod.Builder的字段賦值,從注解轉化為對象。
- 4、內部檢查,保證邏輯正確
- 5、方法參數注解解析,給相應的ServiceMethod.Builder的字段賦值,從注解轉化為對象。
- 6、排除錯誤邏輯。(此時ServiceMethod.Builder的字段已經賦值完畢)
- 7、構造ServiceMethod對象并返回
整個上面流程如下圖:
(6)、Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法詳解:
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 (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
大體流程如下:
- 1、判斷是不是Object的方法,如果是直接invoke
- 2、根據平臺來判斷是否是默認的方法
- 3、調用loadServiceMethod()方法解析數據,如果緩存中有,直接拿
- 4、用serviceMethod和args作為參數,構造一個OkHttpCall對象
- 5、調用serviceMethod的callAdapter對象的adapt(OkHttpCall)方法獲取一個代理實現類的對象。
PS:
- 1、這里說下OkHttpCall是實現retrofit2.Call接口的子類
- 2、serviceMethod.callAdapter是DefaultCallAdapterFactory里面new的匿名內部類,這個匿名內部類的adapt方法直接返回就是retrofit2.Call,因為OkHttpCall是retrofit2.Call的子類,所以返回的是OkHttpCall這個實例本身。
簡易的流程圖如下:
(三)、執行請求
執行請求主要分為兩個部分,一個是執行同步請求,一個是執行異步請求
我們的demo是同步請求,我們先講解同步請求,然后再講解異步請求
1、同步請求詳解
同步代碼如下:
List<Contributor> contributors = call.execute().body();
因為OkHttpCall是實現retrofit2.Call接口的具體實現類,所以
call.execute()實際調用是OkHttpCall的execute()方法
1.1、OkHttpCall的execute()方法詳解
@Override public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
if (creationFailure != null) {
if (creationFailure instanceof IOException) {
throw (IOException) creationFailure;
} else {
throw (RuntimeException) creationFailure;
}
}
call = rawCall;
if (call == null) {
try {
call = rawCall = createRawCall();
} catch (IOException | RuntimeException e) {
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
return parseResponse(call.execute());
}
1.2、OkHttpCall的createRawCall()方法詳解
然后是調用的createRawCall()l來獲取一個okhttp3.Call,然后調用
parseResponse()來獲取retrofit2.Response。
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;
}
- 1、調用serviceMethod的toRequest(args)來得到okhttp3.Request對象
- 2、調用serviceMethod的callFactory對象的newCall(request)來獲取一個okHttpCall。
讓我們看下ServiceMethod類的toRequest()方法
1.3、ServiceMethod類的toRequest()方法詳解
Request toRequest(Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
@SuppressWarnings("unchecked")
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();
}
通過上面代碼我們知道
- 1、構建了一個RequestBuilder的對象
- 2、獲取ServiceMethod的parameterHandlers
- 3、為了保證參數注解和參數的數目的一致性,做了一致性檢查
- 4、遍歷parameterHandlers,根據不同的參數注解,配置RequestBuilder的相應屬性
- 5、最后調用RequestBuilder的build()方法來獲取一個okhttp3.Request對象
那我們來看下RequestBuilder的build()方法
1.4、ServiceMethod類的RequestBuilder的build()方法詳解
Request build() {
HttpUrl url;
HttpUrl.Builder urlBuilder = this.urlBuilder;
if (urlBuilder != null) {
url = urlBuilder.build();
} else {
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 = 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());
}
}
return requestBuilder
.url(url)
.method(method, body)
.build();
}
通過上面代碼我們知道build()方法流程如下:
- 1、根據HttpUrl.Builder來獲取HttpUrl
- 2、根據不同的類型來構造RequestBody
- 3、設置MediaType
- 4、調用RequestBuilder.url(url)和method(method, body)方法來設置RequestBuilder的HttpUrl和請求方式和請求體。
- 5、調用build來來獲取一個okhttp3.Request對象(build()方法內部是直接new的一個Request)
注意事項:
- retrofit2.RequestBuilder類中有一個字段private final okhttp3.Request.Builder requestBuilder; 大家千萬不要弄混了。
-retrofit2.RequestBuilder的構造函數完成一些初始化,由于簡書篇幅的限制,我這里就沒說,大家最好也去看下。
這時候我們再回來看下OkHttpCall中的createRawCall()內部
Request request = serviceMethod.toRequest(args);
這個流程我們已經講解完了。
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
其實就是調用serviceMethod的callFactory對象的newCall(request)方法來獲取一個okhttp3.Call對象,這個流程是在okhttp里面,我就不詳細說了。
這時候我們又回到了OkHttpCall的execute()的里面,最后調用了parseResponse(call.execute()),截止到call.execute()我們一直都是在解析,構造請求,但是到call.execute(),我們正式發起HTTP請求了。parseResponse方法主要是解析響應體。我們一會再講解。
2、異步請求詳解
call.enqueue(new Callback<List<Contributor>>() {
@Override
public void onResponse(Call<List<Contributor>> call, Response<List<Contributor>> response) {
List<Contributor> contributorList = response.body();
for (Contributor contributor : contributorList){
Log.d("login", contributor.getLogin());
Log.d("contributions", contributor.getContributions() + "");
}
}
@Override
public void onFailure(Call<List<Contributor>> call, Throwable t) {
}
});
因為OkHttpCall是實現retrofit2.Call接口的具體實現類,所以
call.enqueue()實際調用是OkHttpCall的enqueue()方法。那我們來看下OkHttpCall的enqueue()方法的源碼
@Override public void enqueue(final Callback<T> callback) {
if (callback == null) throw new NullPointerException("callback == null");
okhttp3.Call call;
Throwable failure;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
call = rawCall = createRawCall();
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response);
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callSuccess(Response<T> response) {
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
其實內部也一樣通過調用createRawCall()(createRawCall()方法內部的實現邏輯,我這里就不說了)方法來獲取okhttp3.Call對象,然后調用okhttp3.Call的enqueue()方法來實現的。如果請求成功則調用parseResponse來解析響應體,解析過程中沒有問題則調用callSuccess()方法,如果解析出現問題則調用callFailure()方法,其實callFailure()內調用的是callback.onFailure()方法;如果在請求過程中出現問題則也一樣調用callback.onFailure()。
四、處理響應
1、Response<T> parseResponse(okhttp3.Response) 方法詳解
處理響應主要是調用Response<T> parseResponse(okhttp3.Response) 方法來解析響應體
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
rawResponse = rawResponse.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
if (code < 200 || code >= 300) {
try {
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse);
} finally {
rawBody.close();
}
}
if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
T body = serviceMethod.toResponse(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
catchingBody.throwIfCaught();
throw e;
}
}
通過研究parseResponse代碼,我們知道:
- 首先,Retrofit是先取出ResponseBody。
- 然后,調用rawResponse.newBuilder()的build()方法構建一個*** 空的 ***響應的體的rawResponse。
- 其次,根據HTTP狀體碼來判斷是否成功與失敗
- 再次,如果成功且是204或者205則說明內容或者重復內容則不用關心響應體,則調用Response.success(null, rawResponse)方法
- 最后,調用ServiceMethod實例的toResponse(catchingBody)方法來獲取一個類型是T的對象
2、ServiceMethod類的parseResponse() 方法詳解
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
可見ServiceMethod類的parseResponse() 方法內部調用的是responseConverter.convert(body),responseConverter我們之前說過是ServiceMethod.Builder的build()方法中獲取的通過調用createResponseConverter()來獲取的,在上文我們知道這里的responseConverter是GsonResponseBodyConverter。那我們來看下GsonResponseBodyConverter的convert的內部實現
@Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
這樣就實現了網絡響應體的反序列化。
自此我們的ServiceMethod的toRespons()方法我們就解析完畢。
3、Response.success(body, rawResponse)方法詳解
public static <T> Response<T> success(T body, okhttp3.Response rawResponse) {
if (rawResponse == null) throw new NullPointerException("rawResponse == null");
if (!rawResponse.isSuccessful()) {
throw new IllegalArgumentException("rawResponse must be successful response");
}
return new Response<>(rawResponse, body, null);
}
這里面很簡單,就是作為參數new了一個retrofit2.Response對象,然我們來看下retrofit2.Response的構造函數
private Response(okhttp3.Response rawResponse, T body, ResponseBody errorBody) {
this.rawResponse = rawResponse;
this.body = body;
this.errorBody = errorBody;
}
至此,Response.success()方法我們分析關閉,至于Response. error()方法分析,請大家看我之前的文章。
所以同步或者異步請求的響應 我們就可以通過 response.body()來獲取對應的對象。
同步流程圖如下:
三、總結
我們再回過頭來再來分析一下這張圖
其實整個Retrofit的流程如下圖:
四、okHttp+Retrofit的整體架構
為了讓大家更好的理解層級的結構,簡單做了下面這張圖,在層級上讓大家更好的理解層級的結構
如果加上大家自定義的interface和里面的方法,大體的結構如下:
如果把上面的這張圖立起來,就是下面這張圖:
看完這三張圖,希望大家對Retrofit的理解能更深一步!