Retrofit
是 square
公司的另外一款普遍流行的網絡請求框架。前面的一篇文章《源碼分析OKHttp執行過程》已經對 OkHttp
網絡請求框架有一個大概的瞭解。今天一樣地對 Retrofit
的源碼進行走讀,對其底層的實現邏輯作到心中有數。java
Retrofit
的項目地址爲:https://github.com/square/retrofitgit
打開項目目錄下的 samples
文件夾,從這裏能夠瀏覽 Retrofit
項目的使用範例。github
在本文中打開SimpleService.java
這個類做爲源碼走讀的入口。這個類很簡單,展現了 Retrofit
的基本用法json
public final class SimpleService { //定義接口請求地址 public static final String API_URL = "https://api.github.com"; //定義接口返回數據的實體類 public static class Contributor { public final String login; public final int contributions; public Contributor(String login, int contributions) { this.login = login; this.contributions = contributions; } } //定義網絡請求接口 public interface GitHub { //這個是請求github項目代碼貢獻者列表的接口 //使用@GET註解指定GET請求,並指定接口請求路徑,使用大括號{}定義的參數,是形參,retrofit會把方法中的 //@Path 傳入到請求路徑中 @GET("/repos/{owner}/{repo}/contributors") Call<List<Contributor>> contributors( @Path("owner") String owner, @Path("repo") String repo); } public static void main(String... args) throws IOException { // 建立一個retrofit,而且指定了接口的baseUrl // 而後設置了一個gson轉換器,用於將接口請求下來的json字符串轉換爲Contributor實體類。 Retrofit retrofit = new Retrofit.Builder() .baseUrl(API_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); // 這裏是魔法所在,retrofit將程序猿定義的接口變成「實現類」 GitHub github = retrofit.create(GitHub.class); //經過retrofit這個「實現類」執行contributors方法 Call<List<Contributor>> call = github.contributors("square", "retrofit"); // 執行Call類中的execute方法,這是一個同步方法 // 固然跟okhttp同樣,異步方法是enqueue,這個下文會提到 List<Contributor> contributors = call.execute().body(); for (Contributor contributor : contributors) { System.out.println(contributor.login + " (" + contributor.contributions + ")"); } } }
經過上面代碼的閱讀,知道 retrofit
使用流程api
API
retrofit
對象,指定baseUrl
和數據轉換器(即接口數據解析器,如對json
、xml
、protobuf
等數據類型的解析)retrofit
將程序猿定義的 API
接口變成"實現類"這個流程關鍵點是四、五、6,下文將詳細對這幾個步驟的源碼進行閱讀。數組
在繼續下文以前,咱們先看看這個SimpleService
的執行結果,它打印了retrofit
這個項目的代碼貢獻者緩存
JakeWharton (928) swankjesse (240) pforhan (48) eburke (36) dnkoutso (26) NightlyNexus (26) edenman (24) loganj (17) Noel-96 (16) rcdickerson (14) rjrjr (13) kryali (9) adriancole (9) holmes (7) swanson (7) JayNewstrom (6) crazybob (6) Jawnnypoo (6) danrice-square (5) vanniktech (5) Turbo87 (5) naturalwarren (5) guptasourabh04 (4) artem-zinnatullin (3) codebutler (3) icastell (3) jjNford (3) f2prateek (3) PromanSEW (3) koalahamlet (3)
從上文的源碼閱讀中,能夠看出程序猿只是定義了一個接口,可是如今實現接口的工做是由 retrofit
來實現的網絡
GitHub github = retrofit.create(GitHub.class); Call<List<Contributor>> call = github.contributors("square", "retrofit");
打開 retrofit.create
方法框架
public <T> T create(final Class<T> service) { //對接口進行校驗 Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); } //經過Proxy建立了一個代理 return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { private final Platform platform = Platform.get(); private final Object[] emptyArgs = new Object[0]; @Override public Object invoke(Object proxy, Method method, @Nullable 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); } //判斷是否爲默認方法,Java8中接口也能夠有默認方法,因此這裏有這個判斷 if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } //關鍵點 return loadServiceMethod(method).invoke(args != null ? args : emptyArgs); } }); }
這個方法很短,關鍵是經過 Proxy
建立了一個 Github
接口的代理類並返回該代理。koa
newProxyInstance
方法須要3個參數:ClassLoader
、Class<?>
數組、InvocationHandler
回調。
這個 InvocationHandler
很是關鍵,當執行接口 Github
的contributors
方法時,會委託給InvocationHandler
的invoke
方法來執行。即Github
將接口代理給了Proxy
來執行了。
接着看InvocationHandler
接口的實現。
在 invoke
方法中有三個參數,其中proxy
就是代理對象,而 method
就是程序猿定義的那個網絡請求接口,顧名思義 args
就是方法的參數。
此方法最終是調用了
loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
打開 loadServiceMethod
方法
ServiceMethod<?> loadServiceMethod(Method method) { // 判斷是否有緩存 ServiceMethod<?> result = serviceMethodCache.get(method); if (result != null) return result; //同步處理 synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); if (result == null) { //沒有獲取到緩存則使用`ServiceMethod`方法來建立 result = ServiceMethod.parseAnnotations(this, method); //最後緩存起來 serviceMethodCache.put(method, result); } } return result; }
這個方法就是經過 method
來獲取一個 ServiceMethod
對象。
打開 ServiceMethod
發現它是一個抽象類,有一個靜態方法 parseAnnotations
和一個抽象方法 invoke
。
abstract class ServiceMethod<T> { static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) { //對註解進行解析 RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); //獲取方法的返回類型 Type returnType = method.getGenericReturnType(); //對返回類型進行校驗 if (Utils.hasUnresolvableType(returnType)) { throw methodError(method, "Method return type must not include a type variable or wildcard: %s", returnType); } if (returnType == void.class) { throw methodError(method, "Service methods cannot return void."); } //最終使用到HttpServiceMethod類 return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); } abstract T invoke(Object[] args); }
parseAnnotations
方法就是對程序猿定義的接口中使用的註解進行解析。
最後是使用了HttpServiceMethod.parseAnnotations
方法
/** Adapts an invocation of an interface method into an HTTP call. */ final class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> { static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) { CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method); //...省略部分代碼 Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType); okhttp3.Call.Factory callFactory = retrofit.callFactory; return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter); } //...省略部分代碼 @Override ReturnT invoke(Object[] args) { return callAdapter.adapt( new OkHttpCall<>(requestFactory, args, callFactory, responseConverter)); } }
HttpServiceMethod
是 ServiceMethod
的子類。而在parseAnnotations
方法中構造了HttpServiceMethod
實例並返回。
所以,loadServiceMethod
方法返回的是HttpServiceMehod
對象
這樣下面代碼的執行其實是執行了 HttpServiceMehod
的 invoke
方法。
loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
再次翻看上文中HttpServiceMethod
類
@Override ReturnT invoke(Object[] args) { return callAdapter.adapt( new OkHttpCall<>(requestFactory, args, callFactory, responseConverter)); }
invoke
方法裏有執行了callAdapter.adapt
方法,參數爲OkHttpCall
,這個類實際上就是對okhttp
網絡請求的封裝,這裏也能夠看出retrofit
內部是使用了okhttp
來執行網絡請求的
public interface CallAdapter<R, T> { //..省略部分代碼 T adapt(Call<R> call); //CallAdapter抽象工廠類 abstract class Factory { //返回CallAdapter實例 public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit); //..省略部分代碼 } }
這是一個接口,內部有一個Factory
抽象工廠類,用於獲取CallAdapter
對象。
CallAdapter
有不少子類,那 callAdapter.adapt
方法執行的是哪一個具體類的方法呢?實際上,從調試代碼中能夠發現是調用DefaultCallFactory
中的內部實現類
final class DefaultCallAdapterFactory extends CallAdapter.Factory { static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory(); @Override public @Nullable CallAdapter<?, ?> get( Type returnType, Annotation[] annotations, Retrofit retrofit) { if (getRawType(returnType) != Call.class) { return null; } final Type responseType = Utils.getCallResponseType(returnType); //返回一個CallAapter實例 return new CallAdapter<Object, Call<?>>() { @Override public Type responseType() { return responseType; } @Override public Call<Object> adapt(Call<Object> call) { //將參數返回,而這個參數就是OKHttpCall的實例 return call; } }; } }
能夠發現,在adapt
方法中就是將參數call
返回。
因此下面代碼返回的是OkHttpCall
對象。
loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
綜上
//建立了Github接口的代理類 GitHub github = retrofit.create(GitHub.class); //執行接口的方法,其實就是調用了代理類的方法,並最終返回了一個OKhttpCall對象 //而這個對象就是對Okhttp的封裝 Call<List<Contributor>> call = github.contributors("square", "retrofit");
上文中獲取到OKhttpCall
對象,它只是把接口請求過程進行了封裝,並無真正的獲取到接口數據。要獲取到接口數據還須要調用OkHttpCall.execute
方法
List<Contributor> contributors = call.execute().body();
這裏的請求過程與前文中《源碼分析OKHttp執行過程》介紹的是相似的。接一下
打開OkHttpCall.execute
方法
@Override public Response<T> execute() throws IOException { okhttp3.Call call; synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; if (creationFailure != null) { if (creationFailure instanceof IOException) { throw (IOException) creationFailure; } else if (creationFailure instanceof RuntimeException) { throw (RuntimeException) creationFailure; } else { throw (Error) creationFailure; } } call = rawCall; if (call == null) { try { call = rawCall = createRawCall(); } catch (IOException | RuntimeException | Error e) { throwIfFatal(e); // Do not assign a fatal error to creationFailure. creationFailure = e; throw e; } } } if (canceled) { call.cancel(); } return parseResponse(call.execute()); }
這裏的執行邏輯也很簡單
synchronized
進行同步操做createRawCall
建立 okhttp3.Call
對象okhttp
的Call.execute
方法,並解析response
後返回請求結果一樣地,異步請求操做也是相似的
打開OkHttpCall.enqueue
方法
@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 { //建立okhttp網絡請求 call = rawCall = createRawCall(); } catch (Throwable t) { throwIfFatal(t); failure = creationFailure = t; } } } if (failure != null) { callback.onFailure(this, failure); return; } if (canceled) { call.cancel(); } //最終是執行了OkHttp中的call.enqueue方法 //並回調相應的接口 call.enqueue(new okhttp3.Callback() { @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) { Response<T> response; try { response = parseResponse(rawResponse); } catch (Throwable e) { throwIfFatal(e); callFailure(e); return; } try { callback.onResponse(OkHttpCall.this, response); } catch (Throwable t) { t.printStackTrace(); } } @Override public void onFailure(okhttp3.Call call, IOException e) { callFailure(e); } private void callFailure(Throwable e) { try { callback.onFailure(OkHttpCall.this, e); } catch (Throwable t) { t.printStackTrace(); } } }); }
這個方法其實最終都是執行了okhttp
的相應方法。
Retrofit
其實一種更加高級的網絡應用框架,經過代理模式簡化了接口的定義,無需提供接口的具體實現就能夠完成網絡接口請求的執行。它的底層其實是封裝了 okhttp
的執行過程,也把對網絡的操做進行了封裝,而對於程序猿來講只須要關注業務邏輯,對網絡請求的具體實現沒必要關心。
例如在本文開頭的實例中咱們只須要定義接口,定義實體類,其餘工做都交給了 Retrofit
,接下來就是Magic
。