Retrofit2.0所以然之原始碼分析
Retrofit2.0之所以然-原始碼解讀
- 原始碼解讀
- 小結
原始碼解讀
上節已經列出可以在專案中實操的retrofit使用方式,本節看一下核心的原始碼,通過對原始碼的認識,可以更瞭解其機制。
retrofit使用的是目前比較流行的流式程式設計。Retrofit中通過內部類Builder構建自身物件。
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
//沒有設定客戶端的話,預設使用OkHttpClient
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
//適配工廠集合,一下兩下解析不清,不新增的話,預設使用DefaultCallAdapterFactory物件,
//正常也使用預設,無需重新改工廠類
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);
}
}
Builder中通過build()來例項化Retrofit物件。可能會看到converterFactories 是一個集合,有麼有興奮的意淫,可以新增多個轉換工廠?但是不幸的告訴您,可以新增,但是在大部分使用場景中預設使用的僅是第一個Converter.Factory。
Retrofit物件生成了,就該create()生成代理物件了。
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
//這裡就是java為大家生成的動態代理物件
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 = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
有了代理物件,代理物件自然可以呼叫自身的介面方法。在動態代理是個什麼梗那章中已經介紹了執行介面方法實際上是執行動態代理中的invoke()方法。核心的為下面三個方法。
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
loadServiceMethod(method)是將定義的介面方法中的註釋了,請求方式了,header頭了等進行解析處理為ServiceMethod物件。其也是通過ServiceMethod的內部類Builder來構造的。
public ServiceMethod build() {
//這裡是通過adapterFactories適配工廠的集合中的第一個適配工廠物件的CallAdapter。
//適配工廠不做太多介紹,能看該文的人的智商是無法重寫適配工廠,
//重要的事情說三遍 使用過程中無需使用addCallAdapterFactory(factory)
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);
}
下來來看看響應資料的轉換吧
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(Converter.Factory skipPast,
Type type, Annotation[] annotations) {
checkNotNull(type, "type == null");
checkNotNull(annotations, "annotations == null");
//skipPast=null
int start = converterFactories.indexOf(skipPast) + 1;
//start=0;
//這裡用來遍歷你新增的所有轉換工廠選擇出合適使用的
for (int i = start, count = converterFactories.size(); i < count; i++) {
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
//noinspection unchecked
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());
}
有人就納悶這裡明明遍歷找最合適的,為啥本文前面強調只能新增一個轉換工廠。
舉個例子說吧!
響應的結果是不可轉換為json串的字串。那如果第一個轉換工廠是GsonConverterFactory呢?他怎麼知道這個格式是json,所以就預設使用GsonConverterFactory來處理,處理肯定報錯。
什麼GsonConverterFactory, SimpleXmlConverterFactory看其名知其用。
ScalarsConverterFactory?
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
if (type == String.class) {
return StringResponseBodyConverter.INSTANCE;
}
if (type == Boolean.class || type == boolean.class) {
return BooleanResponseBodyConverter.INSTANCE;
}
if (type == Byte.class || type == byte.class) {
return ByteResponseBodyConverter.INSTANCE;
}
if (type == Character.class || type == char.class) {
return CharacterResponseBodyConverter.INSTANCE;
}
if (type == Double.class || type == double.class) {
return DoubleResponseBodyConverter.INSTANCE;
}
if (type == Float.class || type == float.class) {
return FloatResponseBodyConverter.INSTANCE;
}
if (type == Integer.class || type == int.class) {
return IntegerResponseBodyConverter.INSTANCE;
}
if (type == Long.class || type == long.class) {
return LongResponseBodyConverter.INSTANCE;
}
if (type == Short.class || type == short.class) {
return ShortResponseBodyConverter.INSTANCE;
}
return null;
}
看到這可以曉得ScalarsConverterFactory到底可以處理什麼資料格式了吧!
好了到這ServiceMethod物件也構建完成。然後到OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);這裡其實構造的是OKhttpClient。這個serviceMethod.callAdapter.adapt(okHttpCall);呢?使用的預設的適配工廠DefaultCallAdapterFactory。
//放如啥返回啥,放入的是OkHttpCall例項,將該物件毫無保留的返回。
@Override public <R> Call<R> adapt(Call<R> call) {
return call;
}
也就是前面的動態代理物件呼叫其介面方法時,其實返回的是一個將介面方法解析的serviceMethod,以及OKhttpClient兩者封裝的OkHttpCall。
然後就到了我們的程式碼,就是
//同步或非同步的呼叫了。
//同步請求
try {
Response<String> response = call.execute();
String body = response.body();
System.out.println(body);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
嗯!剩下的就是呼叫執行了,自行檢視。就這樣整個流程就串起來。
小結
無,無,無