Android技術交流羣653583088,歡迎你們加入交流,暢談!本羣有免費學習資料視頻’
java
簡單使用android
定義HTTP APIgit
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
複製代碼
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
複製代碼
Call<List<Repo>> repos = service.listRepos("octocat");
複製代碼
retrofit實例的建立,使用了builder模式,從下面的源碼中能夠看出
github
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);
}
}
複製代碼
好玩的地方開始了,咱們先來看看這個方法json
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的默認方法進行一個處理,也是在進行後續處理以前進行去噪。其中的關鍵代碼其實就是最後三句,這也是這篇文章將要分析的
api
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)
,功能和其名字同樣,其對方法註解進行了解析bash
/**
* 解析方法註解,嗚啦啦
* 經過判斷註解類型來解析
* @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;
// 能夠迭代,Collection
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()) {// 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,以後構建請求時會用到
異步
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; } } ### ServiceMethod R toResponse(ResponseBody body) throws IOException { return responseConverter.convert(body); } 複製代碼
能夠看出parseResponse最終調用了Converter.convert
方法。這裏以經常使用的GsonConverterFactory爲例。
# GsonConverterFactory
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}
# GsonResponseBodyConverter
final 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,歡迎你們加入交流,暢談!本羣有免費學習資料視頻’