Android技術交流群653583088,歡迎大家加入交流,暢談!本群有免費學習資料視訊’
簡單使用
定義HTTP API
public interface GitHubService {
@GET("users/{user
}/repos") Call<
List<
Repo>
>
listRepos(@Path("user") String user);
}複製程式碼
建立Retrofit並生成API的實現
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build();
GitHubService service = retrofit.create(GitHubService.class);
複製程式碼
呼叫API方法,生成Call
Call<
List<
Repo>
>
repos = service.listRepos("octocat");
複製程式碼
Retrofit的建立
retrofit例項的建立,使用了builder模式,從下面的原始碼中可以看出
public static final class Builder {
Builder(Platform platform) {
this.platform = platform;
converterFactories.add(new BuiltInConverters());
} public Builder() {
// Platform.get()方法可以用於判斷當前的環境 this(Platform.get());
} public Builder baseUrl(String baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
HttpUrl httpUrl = HttpUrl.parse(baseUrl);
if (httpUrl == null) {
throw new IllegalArgumentException("Illegal URL: " + baseUrl);
} return baseUrl(httpUrl);
} public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
} okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
// 新建Client,留到之後newCall什麼的
} 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);
}
}複製程式碼
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() {
// platform 可以分辨出你是在android,還是java8,又或者別的 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. // 這裡的invoke,Object方法都走這裡,比如equals、toString、hashCode什麼的 if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
} // java8預設方法,1.8的新特性 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);
}
});
}複製程式碼
可以看出建立API使用了動態代理,根據介面動態生成的代理類,將介面的都轉發給了負責連線代理類和委託類的InvocationHandler例項,介面方法也都通過其invoke方法來處理。在invoke方法中,首先會通過Platform.get()方法判斷出當前程式碼的執行環境,之後會先把Object和Java8的預設方法進行一個處理,也是在進行後續處理之前進行去噪。其中的關鍵程式碼其實就是最後三句,這也是這篇文章將要分析的
建立ServiceMethod
erviceMethod<
?, ?>
loadServiceMethod(Method method) {
// 從快取裡面取出,如果有的話,直接返回好了 ServiceMethod<
?, ?>
result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
// 為null的話,解析方法的註解和返回型別、引數的註解he引數型別,新建一個ServiceMethod result = new ServiceMethod.Builder<
>
(this, method).build();
// ->
// 新建的ServiceMethod加到快取列表裡面 serviceMethodCache.put(method, result);
}
} return result;
}複製程式碼
註解的解析
CallAdapter
和Converter
等到後面再分析,這裡先看看parseMethodAnnotation(annotation)
,功能和其名字一樣,其對方法註解進行了解析
/** * 解析方法註解,嗚啦啦 * 通過判斷註解型別來解析 * @param annotation */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);
} // 其他的一些方法註解的解析 ...
}private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
if (this.httpMethod != null) {// 已經賦值過了 throw methodError("Only one HTTP method is allowed. Found: %s and %s.", this.httpMethod, httpMethod);
} this.httpMethod = httpMethod;
this.hasBody = hasBody;
// value為設定註解方法時候,設定的值,官方例子中的users/{user
}/repos or user if (value.isEmpty()) {
return;
} // 查詢條件的一些判斷 ... this.relativeUrl = value;
this.relativeUrlParamNames = parsePathParameters(value);
}`複製程式碼
在解析註解時,先通過instanceof判斷出註解的型別,之後呼叫parseHttpMethodAndPath方法解析註解引數值,並設定httpMethod、relativeUrl、relativeUrlParamNames等屬性。上面說了API中方法註解的解析,現在來看看方法引數註解的解析,這是通過呼叫parseParameterAnnotation方法生成ParameterHandler例項來實現的,程式碼比較多,這裡挑選@Query來看看。
else if (annotation instanceof Query) {Query query = (Query) annotation;
String name = query.value();
boolean encoded = query.encoded();
Class<
?>
rawParameterType = Utils.getRawType(type);
// 返回基礎的類gotQuery = true;
// 可以迭代,Collectionif (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()) {// Array Class<
?>
arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
// 如果是基本型別,自動裝箱 Converter<
?, String>
converter = retrofit.stringConverter(arrayComponentType, annotations);
return new ParameterHandler.Query<
>
(name, converter, encoded).array();
} else {// Other Converter<
?, String>
converter = retrofit.stringConverter(type, annotations);
return new ParameterHandler.Query<
>
(name, converter, encoded);
}複製程式碼
在@Query中,將分成Collection、array、other三種情況處理引數,之後根據這些引數,呼叫ParameterHandler中的Query靜態類,建立出一個ParameterHandler例項。這樣迴圈直到解析了所有的引數註解,組合成為全域性變數parameterHandlers,之後構建請求時會用到
OkHttpCall
ServiceMethod
建立完成之後,我們來看看下一行程式碼中的OkHttpCall
類,裡面的包含了請求的執行和響應處理,我們來看看非同步請求的做法
OkHttpCall(ServiceMethod<
T, ?>
serviceMethod, Object[] args) {
this.serviceMethod = serviceMethod;
this.args = args;
}@Override public void enqueue(final Callback<
T>
callback) {checkNotNull(callback, "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();
// 建立OkHttp3.Call
} 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();
}
}
});
}private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
// 根據ParameterHandler組裝Request.Builder,生成Request okhttp3.Call call = serviceMethod.callFactory.newCall(request);
// Retrofit中建立的new OkHttpClient().newCall(request) ... return call;
}複製程式碼
CallAdapter現在來看看enqueue傳入的引數callback,這個引數可能和很多人心中想的並不一樣,它並不是使用者在使用時傳入的那個Callback物件。那麼他是從哪裡來的呢?不知道你還記不記得我之前在Retrofit.Builder.build()方法中提到過一句程式碼Platform.get()。在不使用addCallAdapterFactory的情況下。將會使用Platform的一種內部類,在Android環境下將會使用到Android類(這其實是個策略模式)
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 {
// Looper.getMainLooper()就是為嘛響應會在主執行緒的原因 private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
}複製程式碼
上面的程式碼先稍微放一下,我們繼續看retrofit.Bulider.build
,其中有幾句比較關鍵的程式碼
callFactory = new OkHttpClient();
callbackExecutor = platform.defaultCallbackExecutor();
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
複製程式碼
結合Android類中的程式碼可以看出,其最後生成了ExecutorCallAdapterFactory類。雖然看到了CallAdapter.Factory,但是到底是哪裡執行了enqueue方法呢?現在我們來看看retrofit.create的最後一句程式碼serviceMethod.callAdapter.adapt(okHttpCall)
Converter
現在回到OkhttpCall.enqueue方法中,在其中還有一句重要的程式碼沒有看,那就是response = parseResponse(rawResponse);
,我們來看看這其中做了什麼。
Response<
T>
parseResponse(okhttp3.Response rawResponse) throws IOException ResponseBody rawBody = rawResponse.body();
// Remove the body's source (the only stateful object) so we can pass th rawResponse = rawResponse.newBuilder() .body(new NoContentResponseBody(rawBody.contentType(), rawBody.conte .build();
... ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
T body = serviceMethod.toResponse(catchingBody);
// 解析body,比如Gson解析 return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather // a runtime exception. catchingBody.throwIfCaught();
throw e;
}
}### ServiceMethodR toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}複製程式碼
可以看出parseResponse最終呼叫了Converter.convert
方法。這裡以常用的GsonConverterFactory為例。
# GsonConverterFactory@Overridepublic Converter<
ResponseBody, ?>
responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
TypeAdapter<
?>
adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<
>
(gson, adapter);
}# GsonResponseBodyConverterfinal class GsonResponseBodyConverter<
T>
implements Converter<
ResponseBody, T>
{
private final Gson gson;
private final TypeAdapter<
T>
adapter;
GsonResponseBodyConverter(Gson gson, TypeAdapter<
T>
adapter) {
this.gson = gson;
this.adapter = adapter;
} @Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
}複製程式碼
responseBodyConverter方法中用到的type引數就是之前我在CallAdapter中提到的responseType方法的返回值。生成adapter方法,用於convert方法使用。OkHttpCall在這之後的程式碼就比較簡單了,通過回撥將轉換後得響應資料傳送出去即可本文分析了Retrofit的執行流程,其實包含了Retrofit、ServiceMethod、OkHttpCall、CallAdapter、Converter等方面。Retrofit的程式碼相對是比較少,也比較容易理解的,不過卻是很好的架構例項。
Android技術交流群653583088,歡迎大家加入交流,暢談!本群有免費學習資料視訊’