Android進階必學retrofit原始碼解析
原始碼的分析將從基本的使用方法入手,分析retrofit的實現方案,以及其中涉及到的一些有趣的技巧。
‘Android技術交流群653583088,歡迎大家加入交流,暢談!本群有免費學習資料視訊’並且免費分享retrofit原始碼解析視訊
簡單使用
定義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; // 可以迭代,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,之後構建請求時會用到
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; } } ### 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,歡迎大家加入交流,暢談!本群有免費學習資料視訊’並且免費分享retrofit原始碼解析視訊