Retrofit2 源碼解析

來源:互聯網
上載者:User

標籤:

原文連結:http://bxbxbai.github.io/2015/12/13/retrofit2-analysis/

公司裡最近做的項目中網路架構用的就是Retrofit,用的多了以後覺得這個架構真的非常好用,然後抽了點時間debug了一下源碼,覺得不光代碼寫的非常好,而且設計這個架構的思路都非常特別,收穫很多,決定記錄下來

本文的源碼分析基於Retrofit 2.0,和Retrofit 1.0有較大的不同, 本文主要分為幾部分:0、Retrofit 是什麼,1、Retrofit 怎麼用,2、Retrofit的原理是什麼,3、一些總結

下面說的Retrofit全部指Retrofit 2

0 Retrofit是什麼

來自Retrofit官網的介紹:

A type-safe HTTP client for Android and Java

簡單的說它是一個HTTP請求工具,和Google開發的Volley功能上非常相似,這裡有Volley的源碼解析,但是使用上很不相似。Retrofit使用起來更簡單,Volley使用上更加原始而且符合使用者的直覺,其實我覺得如果對自己Volley封裝一下也可以像Retrofit那樣的簡單的使用

關於Volley的使用方法,請戳上面的Volley源碼解析連結,這裡就不贅述了

1 Retrofit怎麼用

雖然Retrofit官網已經說明了,我還是要按照我的思路說一下它的使用方法

比如你要請求這麼一個api:

https://api.github.com/repos/{owner}/{repo}/contributors

查看github上某個repo的contributors,首先你要這樣建一個介面:

public interface GitHub {    @GET("/repos/{owner}/{repo}/contributors")    Call<List<Contributor>> contributors(        @Path("owner") String owner, @Path("repo") String repo);}

然後你還需要建立一個Retrofit對象:

public static final String API_URL = "https://api.github.com";// Create a very simple REST adapter which points the GitHub API.Retrofit retrofit = new Retrofit.Builder() .baseUrl(API_URL) .addConverterFactory(GsonConverterFactory.create()) .build();

再用這個Retrofit對象建立一個GitHub對象:

// Create an instance of our GitHub API interface.GitHub github = retrofit.create(GitHub.class);// Create a call instance for looking up Retrofit contributors.Call<List<Contributor>> call = github.contributors("square", "retrofit");

最後你就可以用這個Githb對象獲得資料了:

// Fetch and print a list of the contributors to the library.call.enqueue(new Callback<List<Contributor>>() {    @Override    public void onResponse(Response<List<Contributor>> response) { for (Contributor contributor : response.body()) { System.out.println(contributor.login + " (" + contributor.contributions + ")"); } } @Override public void onFailure(Throwable t) { }});

這個使用方式看上去和Volley的方式完全不一樣,使用Volley時你必須先建立一個Request對象,包括這個請求的Method,Url,Url的參數,以及一個請求成功和失敗的Listener,然後把這個請求放到RequestQueue中,最後NetworkDispatcher會請求伺服器獲得資料。而Retrofit只要建立一個介面就可以了,太不可思議了!!

而我要說,其實這兩種方式本質上是一樣的,只是這個架構描述HTTP請求的方式不一樣而已。因此,你可以發現上面的Github介面其實就是Retrofit對一個HTTP請求的描述

2 Retrofit的原理

Volley描述一個HTTP請求是需要建立一個Request對象,而執行這個請求呢,就是把這個請求對象放到一個隊列中,讓網路線程去處理。

Retrofit是怎麼做的呢?答案就是Java的動態代理

動態代理

當開始看Retrofit的代碼,我對下面這句代碼感到很困惑:

// Create an instance of our GitHub API interface.GitHub github = retrofit.create(GitHub.class);

我給Retrofit對象傳了一個Github介面的Class對象,怎麼又返回一個Github對象呢?進入create方法一看,沒幾行代碼,但是我覺得這幾行代碼就是Retrofit的精妙的地方:

/** Create an implementation of the API defined by the {@code service} interface. */@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.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() { 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. if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } return loadMethodHandler(method).invoke(args); } });}

看,create方法重要就是返回了一個動態代理對象。那麼問題來了...

動態代理是個什麼東西?

看Retrofit代碼之前我知道Java動態代理是一個很重要的東西,比如在Spring架構裡大量的用到,但是它有什麼用呢?

Java動態代理就是Java開發給了開發人員一種可能:當你要調用某個類的方法前,插入你想要執行的代碼

比如你要執行某個操作前,你必須要判斷這個使用者是否登入,或者你在付款前,你需要判斷這個人的賬戶中存在這麼多錢。這麼簡單的一句話,我相信可以把一個不懂技術的人也講明白Java動態代理是什麼東西了。

為什麼要使用動態代理

你看上面代碼,擷取資料的代碼就是這句:

// Create a call instance for looking up Retrofit contributors.Call<List<Contributor>> call = github.contributors("square", "retrofit");

上面github對象其實是一個動態代理對象,並不是一個真正的Githb介面的implements對象,當github對象調用contributors方法時,執行的是動態代理方法(你debug一下就知道了)

此時,動態代理髮揮了它的作用,你看上去是調用了contributors方法,其實此時Retrofit把Github介面翻譯成一個HTTP請求,也就是Retrofit中的MethodHandler對象,這個對象中包含了:

  • OkHttpClient:發送網路請求的工具
  • RequestFactory: 類似於Volley中的Request,包含了HTTP請求的Url、Header資訊,MediaType、Method以及RequestAction數組
  • CallAdapter:HTTP請求返回資料的類型
  • Converter:資料轉換器

嗯,簡單來說,Retrofit就是在你調用Call<List<Contributor>> call = github.contributors("square", "retrofit");後為你產生了一個Http請求,然後,你調用call.enqueue方法時就發送了這個請求,然後你就可以處理Response的資料了,從原理上講,就是這樣的。如果要再往細節處說,就可以再說很多了

3 Retrofit的源碼分析

想要弄清楚Retrofit的細節,先來看一下Retrofit源碼的組成:

  1. 一個retrofit2.http包,裡面全部是定義HTTP請求的註解,比如GET、POST、PUT、DELETE、Headers、Path、Query等等
  2. 餘下的retrofit2包中十幾個類和介面就是全部retrofit的代碼了,代碼真的很少,很簡單,因為retrofit把網路請求這部分功能全部交給了okHttp了
Retrofit介面

我覺得你你必須要知道下面介面的含義

Callback<T>

這個介面就是retrofit請求資料返回的介面,只有兩個方法

  • void onResponse(Response<T> response);
  • void onFailure(Throwable t);
Converter<F, T>

這個介面主要的作用就是將HTTP返回的資料解析成Java對象,主要由Xml、Gson、protobuf等等,你可以在建立Retrofit對象時添加你需要使用的Converter實現(看上面建立Retrofit對象的代碼)

Call<T>

這個介面主要的作用就是發送一個HTTP請求,Retrofit預設的實現是OkHttpCall<T>,你可以根據實際情況實現你自己的Call類,這個設計和Volley的HttpStack介面設計的思想非常相似,子類可以實現基於HttpClient或HttpUrlConnetction的HTTP請求工具,這種設計非常的外掛程式化,而且靈活

CallAdapter<T>

上面說到過,CallAdapter中屬性只有responseType一個,還有一個<R> T adapt(Call<R> call)方法,這個介面的實作類別也只有一個,DefaultCallAdapter。這個方法的主要作用就是將Call對象轉換成另一個對象,可能是為了支援RxJava才設計這個類的吧

Retrofit的運行

剛才講到GitHub github = retrofit.create(GitHub.class);代碼返回了一個動態代理對象,而執行Call<List<Contributor>> call = github.contributors("square", "retrofit");代碼時返回了一個OkHttpCall對象,拿到這個Call對象才能執行HTTP請求

其中後一句代碼執行了一個非常複雜的過程

當執行了contributors方法時,Retrofit其實是執行了動態代理的InvocationHandler對象,最後會建立一個MethodHandler對象,這個對象很重要

static MethodHandler<?> create(Retrofit retrofit, Method method) {    CallAdapter<Object> callAdapter = (CallAdapter<Object>) createCallAdapter(method, retrofit);    Type responseType = callAdapter.responseType();    Converter<ResponseBody, Object> responseConverter =        (Converter<ResponseBody, Object>) createResponseConverter(method, retrofit, responseType);    RequestFactory requestFactory = RequestFactoryParser.parse(method, responseType, retrofit);    return new MethodHandler<>(retrofit.client(), requestFactory, callAdapter, responseConverter);}

上面代碼就是建立一個MethodHandler對象,一個MethodHandler對象中包含了4個對象

0. OkHttpClient

這個是Retrofit預設產生的

1. RequestFactory:

通過RequestFactoryParser.parse(method, responseType, retrofit);產生,主要作用就是解析整個Http請求的所有資料

主要原理就是解析一個介面,比如上面的Github介面,結果就是得到整個Http請求全部的資訊,還會通過@Path和@Query註解拼接Url

不細講了,全部代碼都在retrofit.RequestFactoryParser類中

2. CallAdapter

擷取CallAdapter的代碼如下:

private static CallAdapter<?> createCallAdapter(Method method, Retrofit retrofit) {  Type returnType = method.getGenericReturnType();  if (Utils.hasUnresolvableType(returnType)) {    throw Utils.methodError(method,      "Method return type must not include a type variable or wildcard: %s", returnType); } if (returnType == void.class) { throw Utils.methodError(method, "Service methods cannot return void."); } Annotation[] annotations = method.getAnnotations(); try { return retrofit.callAdapter(returnType, annotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw Utils.methodError(e, method, "Unable to create call adapter for %s", returnType); }}

你可以在建立Retrofit對象時,添加你想要的CallAdapter,而擷取CallAdapter的方式也是從Retrofit對象中擷取

預設的DefaultCallAdapter幾乎沒有上面作用,基本和動畫裡面的LinearInterpolator差不多

3. Converter

獲得Converter對象和上面的原理幾乎一樣

private static Converter<ResponseBody, ?> createResponseConverter(Method method,  Retrofit retrofit, Type responseType) {  Annotation[] annotations = method.getAnnotations();  try {    return retrofit.responseBodyConverter(responseType, annotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw Utils.methodError(e, method, "Unable to create converter for %s", responseType); }}

建立這4個對象的目的就是為了執行下面這句代碼

Object invoke(Object... args) {  return callAdapter.adapt(new OkHttpCall<>(client, requestFactory, responseConverter, args));}

這個也就是github.contributors("square", "retrofit");返回的Call對象

最後你調用Call對象的execute()或enqueue(Callback<T> callback)方法,就能發送一個Http請求了

只不過前一種方式是同步的,後一種是非同步,也就是說Retrofit提供了同步和非同步兩種HTTP請求方式

你可能會覺得我只要發送一個HTTP請求,你要做這麼多事情不會很“慢”嗎?不會很浪費效能嗎?

我覺得,首先現在手機處理器主頻非常高了,解析這個介面可能就花1ms可能更少的時間(我沒有測試過),面對一個HTTP本來就需要幾百ms,甚至幾千ms來說不值得一提;而且Retrofit會對解析過的請求進行緩衝,就在Map<Method, MethodHandler<?>> methodHandlerCache這個對象中

4 最後

Retrofit非常巧妙的用註解來描述一個HTTP請求,將一個HTTP請求抽象成一個Java介面,然後用了Java動態代理的方式,動態將這個介面的註解“翻譯”成一個HTTP請求,最後再執行這個HTTP請求

Retrofit的功能非常多的依賴Java反射,代碼中其實還有很多細節,比如異常的捕獲、拋出和處理,大量的Factory設計模式(為什麼要這麼多使用Factory模式?)

Retrofit中介面設計的恰到好處,在你建立Retrofit對象時,讓你有更多更靈活的方式去處理你的需求,比如使用不同的Converter、使用不同的CallAdapter,這也就提供了你使用RxJava來調用Retrofit的可能

我也慢慢看了Picasso和Retrofit的代碼了,收穫還是很多的,也更加深入的理解面向介面的編程方法,這個寫代碼就是好的代碼就是依賴介面而不是實現最好的例子

好感謝開源的世界,讓我能讀到大牛的代碼。我一直覺得一個人如果沒有讀過好的代碼是不太可能寫出好代碼的。什麼是好的代碼?像Picasso和Retrofit這樣的就是好的代碼,擴充性強、低耦合、外掛程式化



文/白瓦力(簡書作者)
原文連結:http://www.jianshu.com/p/c1a3a881a144
著作權歸作者所有,轉載請聯絡作者獲得授權,並標註“簡書作者”。

Retrofit2 源碼解析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.