Retrofit 實現分析
Retrofit 為我們提供了一種非常優雅的方式去書寫 Restful 的請求的介面代碼,和 OkHttp 、Rxjava 都能方便的無縫搭配,為我們在 Java 和 Android 提供了非常便捷的網路請求的編寫方式。這篇文章中我們會從 Usage 出發,逐個步驟的分析 Retrofit 的實現方式。
實現分析
我們可以定義這樣的一個介面,代表一種 restful 請求:
public interface GitHubService {n @GET("users/{user}/repos")n Call<List<Repo>> listRepos(@Path("user") String user);n}n
在使用 Retrofit 的時候:
Retrofit retrofit = new Retrofit.Builder()n .baseUrl("https://api.github.com/")n .build();nnGitHubService service = retrofit.create(GitHubService.class);n
我們通過 Builder 模式拼好 baseUrl 等字串,通過 retrofit 對象可以創建我們的介面對應的實體類,我們通過對這個實體類的操作,就能對我們定義好的介面去請求對應的數據:
Call<List<Repo>> repos = service.listRepos("octocat");n
創建請求類
@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.n public <T> T create(final Class<T> service) {n Utils.validateServiceInterface(service);n if (validateEagerly) {n eagerlyValidateMethods(service);n }n return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service}, new InvocationHandler() {n // ... 省略n }n n });n }n
這裡我們先是對 class 對象的類型做了檢測保證了是 Interface 而且沒有多繼承,並且在開了 validateEagerly 的情況下會對 Service 裡面的請求介面進行 Cache:
private void eagerlyValidateMethods(Class<?> service) {n Platform platform = Platform.get();n for (Method method : service.getDeclaredMethods()) {n if (!platform.isDefaultMethod(method)) {n loadServiceMethod(method);n }n }n }nn ServiceMethod<?, ?> loadServiceMethod(Method method) {n ServiceMethod<?, ?> result = serviceMethodCache.get(method);n if (result != null) return result;nn synchronized (serviceMethodCache) {n result = serviceMethodCache.get(method);n if (result == null) {n result = new ServiceMethod.Builder<>(this, method).build();n serviceMethodCache.put(method, result);n }n }n return result;n }n
動態代理
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service},n new InvocationHandler() {n private final Platform platform = Platform.get();nn @Overriden public Object invoke(Object proxy, Method method, @Nullable Object[] args)n throws Throwable {n // If the method is a method from Object then defer to normal invocation.n if (method.getDeclaringClass() == Object.class) {n return method.invoke(this, args);n }n if (platform.isDefaultMethod(method)) {n return platform.invokeDefaultMethod(method, service, proxy, args);n }n ServiceMethod<Object, Object> serviceMethod =n (ServiceMethod<Object, Object>) loadServiceMethod(method);n OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);n return serviceMethod.callAdapter.adapt(okHttpCall);n }n });n
這就是剛才在 Create 函數中的省略的部分,從這個寫法看了明顯是用了動態代理的方式。使用代理的方式當然是為了能統一的對我們 Service 進去的方法做一些操作,比如判斷是否走 method 的正常的 invoke 方法,判斷了是否是 default 方法,當然這個根本就不支持,然後就是正常的綁定成一個 ServiceMethod 裡面存儲和請求的各種元信息,最後把他們封裝成一個 OkHttp 的請求交給 Adapt 託管,這樣我們就完成了整個請求服務類的創建。
ServiceMethod
我們可以來看下這個包含元信息的 ServiceMethod 是怎麼構建的:
ServiceMethod<?, ?> loadServiceMethod(Method method) {n ServiceMethod<?, ?> result = serviceMethodCache.get(method);n if (result != null) return result;nn synchronized (serviceMethodCache) {n result = serviceMethodCache.get(method);n if (result == null) {n result = new ServiceMethod.Builder<>(this, method).build();n serviceMethodCache.put(method, result);n }n }n return result;n }n
使用了 ServiceMethod 的默認 build 方法:
Builder(Retrofit retrofit, Method method) {n this.retrofit = retrofit;n this.method = method;n this.methodAnnotations = method.getAnnotations();n this.parameterTypes = method.getGenericParameterTypes();n this.parameterAnnotationsArray = method.getParameterAnnotations();n }n
在這之中我們綁定了很多的東西,綁定了我們對這個方法加的註解,參數類型,還有參數的註解,之後在 build 方法之中,主要的事情就是生成 CallAdapter , 還有就是為我們的 Response 提供類型轉化的 Converter (比如和 Rxjava 一同使用的就會需要這個)。
CallAdapter
在生成的 CallAdapter 的探索路徑中我們會發現是在 Retrofit 的 build 創建的:
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));n
我們的默認的 CallAdapter.Factory 是從 Platform 中提供的:
CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {n if (callbackExecutor != null) {n return new ExecutorCallAdapterFactory(callbackExecutor);n }n return DefaultCallAdapterFactory.INSTANCE;n }nn/**n * Creates call adapters for that uses the same thread for both I/O and application-leveln * callbacks. For synchronous calls this is the application thread making the request; forn * asynchronous calls this is a thread provided by OkHttps dispatcher.n */nfinal class DefaultCallAdapterFactory extends CallAdapter.Factory {n static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();nn @Overriden public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {n if (getRawType(returnType) != Call.class) {n return null;n }nn final Type responseType = Utils.getCallResponseType(returnType);n return new CallAdapter<Object, Call<?>>() {n @Override public Type responseType() {n return responseType;n }nn @Override public Call<Object> adapt(Call<Object> call) {n return call;n }n };n }n}n
從注釋裡面我們也能看出來這個是使用相同的線程去創建 CallBack 和 開 IO ,如果是非同步的 OKHTTP 就是從它的默認的分發的線程,如果是同步調用就和應用用同一個線程。
public interface CallAdapter<R, T> {n /**n * Returns the value type that this adapter uses when converting the HTTP response body to a Javan * object. For example, the response type for {@code Call<Repo>} is {@code Repo}. This typen * is used to prepare the {@code call} passed to {@code #adapt}.n * <p>n * Note: This is typically not the same type as the {@code returnType} provided to this calln * adapters factory.n */n Type responseType();nn /**n * Returns an instance of {@code T} which delegates to {@code call}.n * <p>n * For example, given an instance for a hypothetical utility, {@code Async}, this instance wouldn * return a new {@code Async<R>} which invoked {@code call} when run.n * <pre><code>n * &#64;Overriden * public &lt;R&gt; Async&lt;R&gt; adapt(final Call&lt;R&gt; call) {n * return Async.create(new Callable&lt;Response&lt;R&gt;&gt;() {n * &#64;Overriden * public Response&lt;R&gt; call() throws Exception {n * return call.execute();n * }n * });n * }n * </code></pre>n */n T adapt(Call<R> call);n}n
另外我們看這個生成的 CallAdapter 本身做了什麼事,它判斷了返回的 Response 的類型,針對類型去泛化了一個對應的 CallAdapter , Adapter 做得事情就是把提供介面在 adapter 的過程中給 Call 增加代理(默認的工廠方法沒有提供),另外就是提供類型信息當 Response 回來的時候能進行正確的類型轉化。
Converter
Response Converter 的設計思路和 Adapter 的思路其實是差不多的提供默認的介面,並且輔助包中提供了大量的額外實現:
/**n * Convert objects to and from their representation in HTTP. Instances are created by {@linkplainn * Factory a factory} which is {@linkplain Retrofit.Builder#addConverterFactory(Factory) installed}n * into the {@link Retrofit} instance.n */npublic interface Converter<F, T> {n T convert(F value) throws IOException;nn /**n * Creates {@link Converter} instances based on a type and target usage.n */n abstract class Factory {n /**n * Returns a {@link Converter} for converting an HTTP response body to {@code type}, or null ifn * {@code type} cannot be handled by this factory. This is used to create converters forn * response types such as {@code SimpleResponse} from a {@code Call<SimpleResponse>}n * declaration.n */n public @Nullablen Converter<ResponseBody, ?> responseBodyConverter(Type type,n Annotation[] annotations, Retrofit retrofit) {n return null;n }nn /**n * Returns a {@link Converter} for converting {@code type} to an HTTP request body, or null ifn * {@code type} cannot be handled by this factory. This is used to create converters for typesn * specified by {@link Body @Body}, {@link Part @Part}, and {@link PartMap @PartMap}n * values.n */n public @Nullablen Converter<?, RequestBody> requestBodyConverter(Type type,n Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {n return null;n }nn /**n * Returns a {@link Converter} for converting {@code type} to a {@link String}, or null ifn * {@code type} cannot be handled by this factory. This is used to create converters for typesn * specified by {@link Field @Field}, {@link FieldMap @FieldMap} values,n * {@link Header @Header}, {@link HeaderMap @HeaderMap}, {@link Path @Path},n * {@link Query @Query}, and {@link QueryMap @QueryMap} values.n */n public @Nullablen Converter<?, String> stringConverter(Type type, Annotation[] annotations,n Retrofit retrofit) {n return null;n }nn /**n * Extract the upper bound of the generic parameter at {@code index} from {@code type}. Forn * example, index 1 of {@code Map<String, ? extends Runnable>} returns {@code Runnable}.n */n protected static Type getParameterUpperBound(int index, ParameterizedType type) {n return Utils.getParameterUpperBound(index, type);n }nn /**n * Extract the raw class type from {@code type}. For example, the type representingn * {@code List<? extends Runnable>} returns {@code List.class}.n */n protected static Class<?> getRawType(Type type) {n return Utils.getRawType(type);n }n }n}n
真對不同需求的實現可以去實現不同的方法,比如 rxjava2 包里的 StringConverterFactory 就默認只實現了和 String 相關的方法:
final class StringConverterFactory extends Converter.Factory {n @Overriden public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,n Retrofit retrofit) {n return new Converter<ResponseBody, String>() {n @Override public String convert(ResponseBody value) throws IOException {n return value.string();n }n };n }nn @Override public Converter<?, RequestBody> requestBodyConverter(Type type,n Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {n return new Converter<String, RequestBody>() {n @Override public RequestBody convert(String value) throws IOException {n return RequestBody.create(MediaType.parse("text/plain"), value);n }n };n }n}n
這裡面提供了吧 ResponseBody 轉為 String 的 Converter 和把 String 構建成 ResponseBody 的方法,比如我們經常用的 Gson 的 Converter:
/**n * A {@linkplain Converter.Factory converter} which uses Gson for JSON.n * <p>n * Because Gson is so flexible in the types it supports, this converter assumes that it can handlen * all types. If you are mixing JSON serialization with something else (such as protocol buffers),n * you must {@linkplain Retrofit.Builder#addConverterFactory(Converter.Factory) add this instance}n * last to allow the other converters a chance to see their types.n */npublic final class GsonConverterFactory extends Converter.Factory {n /**n * Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON andn * decoding from JSON (when no charset is specified by a header) will use UTF-8.n */n public static GsonConverterFactory create() {n return create(new Gson());n }nn /**n * Create an instance using {@code gson} for conversion. Encoding to JSON andn * decoding from JSON (when no charset is specified by a header) will use UTF-8.n */n @SuppressWarnings("ConstantConditions") // Guarding public API nullability.n public static GsonConverterFactory create(Gson gson) {n if (gson == null) throw new NullPointerException("gson == null");n return new GsonConverterFactory(gson);n }nn private final Gson gson;nn private GsonConverterFactory(Gson gson) {n this.gson = gson;n }nn @Overriden public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,n Retrofit retrofit) {n TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));n return new GsonResponseBodyConverter<>(gson, adapter);n }nn @Overriden public Converter<?, RequestBody> requestBodyConverter(Type type,n Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {n TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));n return new GsonRequestBodyConverter<>(gson, adapter);n }n}n
裡面則提供了對 Gson 的轉化,因為 Type 信息在之前已經拿到了,所以 Gson 的轉換也非常簡單。
ParameterHandlers
parseParameterAnnotation 根據註解和類型分門別類的對每個參數對應了一個 ParameterHandler ,這段過程沒有太多可說的地方,基本上就是對不同類型的一個大的判斷和 Parser ,ParameterHandler 中有不同的 apply 方法對我們不同的類型進行處理:
abstract void apply(RequestBuilder builder, @Nullable T value) throws IOException;n
最終的目的都是為了為 RequestBuilder 這個對象拼接需要的元信息。
發送請求
Call<List<Repo>> repos = service.listRepos("octocat");n
剛才我們提到了發送請求就是直接這樣使用的就可以把我們的請求發送出來了,我們剛才提到了很多請求用到的 元數據 的綁定,那我們現在應該已經有了一個充滿了請求所需數據的 ResponseBuilder 我們需要的就是調用剛才綁定的那個 OkHttpCall 了:
execute()
@Overridenpublic Response<T> execute() throws IOException {n okhttp3.Call call;nn synchronized (this) {n if (executed) throw new IllegalStateException("Already executed.");n executed = true;nn if (creationFailure != null) {n if (creationFailure instanceof IOException) {n throw (IOException) creationFailure;n } else {n throw (RuntimeException) creationFailure;n }n }nn call = rawCall;n if (call == null) {n try {n call = rawCall = createRawCall();n } catch (IOException | RuntimeException e) {n creationFailure = e;n throw e;n }n }n }nn if (canceled) {n call.cancel();n }nn return parseResponse(call.execute());n}nnprivate okhttp3.Call createRawCall() throws IOException {n Request request = serviceMethod.toRequest(args);n okhttp3.Call call = serviceMethod.callFactory.newCall(request);n if (call == null) {n throw new NullPointerException("Call.Factory returned null.");n }n return call;n}nn Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {n ResponseBody rawBody = rawResponse.body();nn // Remove the bodys source (the only stateful object) so we can pass the response along.n rawResponse = rawResponse.newBuilder()n .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))n .build();nn int code = rawResponse.code();n if (code < 200 || code >= 300) {n try {n // Buffer the entire body to avoid future I/O.n ResponseBody bufferedBody = Utils.buffer(rawBody);n return Response.error(bufferedBody, rawResponse);n } finally {n rawBody.close();n }n }nn if (code == 204 || code == 205) {n rawBody.close();n return Response.success(null, rawResponse);n }nn ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);n try {n T body = serviceMethod.toResponse(catchingBody);n return Response.success(body, rawResponse);n } catch (RuntimeException e) {n // If the underlying source threw an exception, propagate that rather than indicating it wasn // a runtime exception.n catchingBody.throwIfCaught();n throw e;n }n }n
execute() 方法實現了同步請求的方法,這其中的網路請求和調用自然是全靠 OkHttp 來實現的,另外本身使用 OkHttp 的 execute 方法就是一個阻塞的方法。其中值得注意的是我們對 Request 和 Response 的處理,分別是用了 ServiceMethod 中的方法:
/** Builds an HTTP request from method arguments. */n Request toRequest(@Nullable Object... args) throws IOException {n RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,n contentType, hasBody, isFormEncoded, isMultipart);nn @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.n ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;nn int argumentCount = args != null ? args.length : 0;n if (argumentCount != handlers.length) {n throw new IllegalArgumentException("Argument count (" + argumentCountn + ") doesnt match expected count (" + handlers.length + ")");n }nn for (int p = 0; p < argumentCount; p++) {n handlers[p].apply(requestBuilder, args[p]);n }nn return requestBuilder.build();n }nn /** Builds a method return value from an HTTP response body. */n R toResponse(ResponseBody body) throws IOException {n return responseConverter.convert(body);n }n
一個是用了剛才 ParameterHandler 抓到的元信息去構造 Request,另外一個就是把 Response 用對應的 Converter 去解析成想要的類型。
enqueue
@Overriden public void enqueue(final Callback<T> callback) {n checkNotNull(callback, "callback == null");nn okhttp3.Call call;n Throwable failure;nn synchronized (this) {n if (executed) throw new IllegalStateException("Already executed.");n executed = true;nn call = rawCall;n failure = creationFailure;n if (call == null && failure == null) {n try {n call = rawCall = createRawCall();n } catch (Throwable t) {n failure = creationFailure = t;n }n }n }nn if (failure != null) {n callback.onFailure(this, failure);n return;n }nn if (canceled) {n call.cancel();n }nn call.enqueue(new okhttp3.Callback() {n @Overriden public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)n throws IOException {n Response<T> response;n try {n response = parseResponse(rawResponse);n } catch (Throwable e) {n callFailure(e);n return;n }n callSuccess(response);n }nn @Overriden public void onFailure(okhttp3.Call call, IOException e) {n try {n callback.onFailure(OkHttpCall.this, e);n } catch (Throwable t) {n t.printStackTrace();n }n }nn private void callFailure(Throwable e) {n try {n callback.onFailure(OkHttpCall.this, e);n } catch (Throwable t) {n t.printStackTrace();n }n }nn private void callSuccess(Response<T> response) {n try {n callback.onResponse(OkHttpCall.this, response);n } catch (Throwable t) {n t.printStackTrace();n }n }n });n }n
相應的 enqueue 方法也是在一堆的 check 之後,直接使用了 OkHttp 的非同步 enqueue 去請求。
再看 CallAdapter
CallAdapter 剛才我們已經看了它的默認的實現,將 Call<R> 類型,轉換為 T ,當然這裡默認的實現裡面 這個 T 還是 Call<R> ,但是這是默認實現,retrofit 中提供了為數眾多的 adapters ,比如針對 Rxjava 的 Adapter:
@Overriden public Object adapt(Call<R> call) {n Observable<Response<R>> responseObservable = isAsyncn ? new CallEnqueueObservable<>(call)n : new CallExecuteObservable<>(call);nn Observable<?> observable;n if (isResult) {n observable = new ResultObservable<>(responseObservable);n } else if (isBody) {n observable = new BodyObservable<>(responseObservable);n } else {n observable = responseObservable;n }nn if (scheduler != null) {n observable = observable.subscribeOn(scheduler);n }nn if (isFlowable) {n return observable.toFlowable(BackpressureStrategy.LATEST);n }n if (isSingle) {n return observable.singleOrError();n }n if (isMaybe) {n return observable.singleElement();n }n if (isCompletable) {n return observable.ignoreElements();n }n return observable;n }n
就為我們提供了返回 Observable 對象的返回 Adapter。
總結
至此我們就完成了對 Retrofit 的本體的分析,當然 Retrofit 還提供了非常多的 Adapter 和 Converter ,不過都是針對不同的請求類型進行的特化,這裡就不一併分析了。Retrofit 本身的本體代碼非常的少,但是能為我們提供這麼 Restful 的 API 支持,不得不說確實有其過人之處。
閱讀本文你學到了什麼:
- Retrofit 源碼的分析
- 通過動態代理為 API 提供統一操作的思路
- 使用註解提供元信息的請求框架的設計思路
- 使用 Factory 對整個架構留出擴充空間和解耦的思路
推薦閱讀:
※2016年 CSS 庫、框架和工具新生榜 TOP 50
※PHP模版引擎,框架有什麼區別,各有什麼用?
※從用緩存優化函數性能說說第三方框架的使用
※框架到底是個什麼東西?
※業界主流的RPC框架有哪些?Dubbo與Hadoop RPC的區別?