簡略分析Android的Retrofit應用開發架構源碼_Android

來源:互聯網
上載者:User

面對一個項目,對於Android應用開發架構的選擇,我想過三種方案:
1.使用Loader + HttpClient + GreenDao + Gson + Fragment,優點是可定製性強,由於使用Google家自己的Loader和LoaderManager,代碼健壯性強。
缺點是整套代碼學習成本較高,使用過程中樣板代碼較多,(比如每一個Request都需要產生一個新類)
2.Volley,作為Google在IO大會上得瑟過的一個網路程式庫,其實不算什麼新東西(2013 IO發布),使用較為簡單,請求可以取消,可以提供優先順序請求,看起來還是不錯的。
3.Retrofit,一款為了使請求極度簡單化的REST API Client,呼聲也很高,使用門檻幾乎是小白型。
如何選擇呢?首先幹掉1,因為對新人的學習成本確實太高,如果要快速開發一個項目,高學習成本是致命的,同時使用起來樣板代碼很多。

那麼如何在Volley和Retrofit中選擇呢?儘管網上有很多文章在介紹兩個架構的使用方法,而對於其原理,特別是對比分析較少,如果你手裡有一個項目,如何選擇網路模組呢?
首先說明一下這兩個網路架構在項目中的層次:

從上圖可知,不管Volley還是Retrofit,它們都是對現有各種方案進行整合,並提供一個友好,快速開發的方案,在整合過程中,各個模組都可以自行定製 或者替換。比如還原序列化的工作,再比如HttpClient。

而在本文我們將簡略地來看一下Retrofit的源碼部分。


注意,本文並不是使用Retrofit的協助文檔,建議先看Retrofit的文檔和OkHttp的文檔,這些對於理解餘下部分很重要。

使用Retrofit發送一個請求
假設我們要從這個地址 http://www.exm.com/search.json?key=retrofit中擷取如下Json返回:

 {  "data": [        {         "title":"Retrofit使用簡介",         "desc":"Retrofit是一款面向Android和Java的HttpClient",         "link":"http://www.exm.com/retrofit"        },        {         "title":"Retrofit使用簡介",         "desc":"Retrofit是一款面向Android和Java的HttpClient",         "link":"http://www.exm.com/retrofit"        },        {         "title":"Retrofit使用簡介",         "desc":"Retrofit是一款面向Android和Java的HttpClient",         "link":"http://www.exm.com/retrofit"        }      ] }

1.引入依賴

compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'//gson解析compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'

2.配置Retrofit

Retrofit retrofit = new Retrofit.Builder()          .baseUrl("http://www.exm.com")          .addConverterFactory(GsonConverterFactory.create())          .client(new OkHttpClient())          .build();

3.建立Model類 SearchResult來解析結果

4.建立請求介面
Retrofit使用註解來定義一個請求,在方法上面指定請求的方法等資訊,在參數中指定參數等資訊。

public interface RestApi {    @GET("/search.json")    Call<List<SearchResult>> search(      @Query("key") String key       );      //可以定義其它請求      @GET("/something.json")      Call<SomeThing> dosomething(          @Query("params") long params          .......          .......       );}

5.發送請求,我們可以發送同步請求(阻塞當前線程)和非同步請求,並在回調中處理請求結果。

 RestApi restApi = retrofit.create(RestApi.class); Call<List<SearchResult>> searchResultsCall = resetApi.search("retrofit"); //Response<List<SearchResult> searchResults = searchResultsCall.execute();  同步方法 searchResultsCall.enqueue(new Callback<List<SearchResult>>() {        @Override        public void onResponse(Response<List<SearchResult>> response, Retrofit retrofit) {          content.setText(response.body().toString());        }        @Override        public void onFailure(Throwable t) {          content.setText("error");        }      });

Retrofit源碼分析
Retrofit整個項目中使用了動態代理和靜態代理,如果你不太清楚代理模式,建議先google一下,如果對於Java的動態代理原理不是太熟悉,強烈建議先看:這篇文章-戲說代理和Java動態代理
ok,下面按照我們使用Retrofit發送請求的步驟來:

RestApi restApi = retrofit.create(RestApi.class);  //產生一個RestApi的執行個體

輸入一個介面,直接輸出一個執行個體。

這裡岔開說一句,現在隨便在百度上搜一下Java動態代理,出來一堆介紹AOP(面向切面編程)和Spring,導致一部分人本末倒置,認為動態代理幾乎等於AOP,甚至有些人認為動態代理是專門在一個函數執行前和執行後添加一個操作,比如統計時間(因為現在幾乎所有介紹動態代理的地方都有這個例子),害人不淺。實際上動態代理是JDK提供的API,並不是由這些上層建築決定的,它還可以做很多別的事情,Retrofit中對動態代理的使用就是佐證。
摟一眼這裡的源碼,再次建議,如果這裡代碼看不明白,先看看上面提到的那篇文章:

public <T> T create(final Class<T> service) { //返回一個動態代理類的執行個體 return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },  //這個InvocationHandler是關鍵所在,以後調用restapi介面中的方法都會被發送到這裡  new InvocationHandler() {   private final Platform platform = Platform.get();   @Override    public Object invoke(Object proxy, Method method, Object... args)     throws Throwable {    /* 如果是Object的方法,如toString()等,直接調用InvocationHandler的方法,     *注意,這裡其實是沒有任何意義的,因為InvocationHandler其實是一個命令傳送者     *在動態代理中,這些方法是沒有任何語義的,所以不需要在意     */    if (method.getDeclaringClass() == Object.class) {     return method.invoke(this, args);    }    //對於Java8的相容,在Android中不需要考慮    if (platform.isDefaultMethod(method)) {     return platform.invokeDefaultMethod(method, service, proxy, args);    }    //返回一個Call對象    return loadMethodHandler(method).invoke(args);   }   });}

我們可以看到Retrofit.create()之後,返回了一個介面的動態代理類的執行個體,那麼我們調用這個代理類的方法時,調用自然就被發送到我們定義的InvocationHandler中,所以調用

Call<List<SearchResult>> searchResultsCall = resetApi.search("retrofit"); 

時,直接調用到InvocationHandler的invoke方法,下面是invoke此時的上下文:

  @Override    public Object invoke(Object proxy, Method method, Object... args)     throws Throwable {     //proxy對象就是你在外面調用方法的resetApi對象     //method是RestApi中的函數定義,     //據此,我們可以擷取定義在函數和參數上的註解,比如@GET和註解中的參數     //args,實際參數,這裡傳送的就是字串"retrofit"    //這裡必然是return 一個Call對象    return loadMethodHandler(method).invoke(args);   }

此時,invoke的返回必然是一個Call,Call是Retrofit中對一個Request的抽象,由此,大家應該不難想象到loadMethodHandler(method).invoke(args); 這句代碼應該就是去解析介面中傳進來的註解,並產生一個OkHttpClient中對應的請求,這樣我們調用searchResultsCall時,調用OkHttpClient走網路即可。確實,Retrofit的主旋律的確就是這樣滴。

注意,實際上Call,CallBack這種描述方式是在OkHttp中引入的,Retrofit底層使用OkHttp所以也是使用這兩個類名來抽象一個網路請求和一個請求回來之後的回調。總體來看,Retrofit中的Call Callback持有一個OkHttp的Call Callback,將對Retrofit中的各種調用轉寄到OkHttp的類庫中,實際上這裡就是靜態代理啦,因為我們會定義各種代理類,比如OkHttpCall

注 下文中如不無明確支出,則所有的Call,CallBack都是Retrofit中的類
MethodHandler類
MethodHandler類,它是Retrofit中最重要的抽象了,每一個MethodHandler對應於本例的RestApi中的一個每個方法代表的請求以及和這個請求相關其它配置,我們來看看吧。

//這個OkHttp的工廠,用於產生一個OkHttp類庫中的Call,實際上就是傳入配置的Builder的OkHttpClientprivate final okhttp3.Call.Factory callFactory;//通過Method中的註解和傳入的參數,組建一個OkHttp的Requestprivate final RequestFactory requestFactory;//用於對Retrofit中的Call進行代理private final CallAdapter<?> callAdapter;//用於還原序列化返回結果private final Converter<ResponseBody, ?> responseConverter;// 返回一個Call對象Object invoke(Object... args) {   return callAdapter.adapt(new OkHttpCall<>(callFactory, requestFactory, args, responseConverter));}

在Retrofit中通過添加Converter.Factory來為Retrofit添加請求和響應的資料編碼和解析。所以我們可以添加多個Converter.Factory為Retrofit提供處理不同資料的功能。

CallAdapter 可以對執行的Call進行代理,這裡是靜態代理。我們也可以通過添加自己的CallAdapter來作一些操作,比如為請求加上緩衝:

 new CallAdapter.Factory {   @Override     public <R> Call<R> adapt(Call<R> call) { return call;} }class CacheCall implements Call {  Call delegate;   CacheCall(Call call) {     this.delegate = call;  }  @Override  public void enqueue(Callback<T> callback) {    //查看是否有緩衝,否則直接走網路    if(cached) {      return cache;    }    this.delegate.enqueue(callback);  }}

至此,我們在調用resetApi.search("retrofit");時,實際上調用的層層代理之後的OkHttpCall,它是MethodHandler中invoke的時候塞入的。看看OkHttpCall中的代碼吧:

@Override public void enqueue(final Callback<T> callback) { okhttp3.Call rawCall; try {  //建立一個okhttp的Call  rawCall = createRawCall(); } catch (Throwable t) {  callback.onFailure(t);  return; } //直接調用okhttp的入隊操作rawCall.enqueue(new okhttp3.Callback() { private void callFailure(Throwable e) {  try {   callback.onFailure(e);  } catch (Throwable t) {   t.printStackTrace();  } } private void callSuccess(Response<T> response) {  try {   callback.onResponse(response);  } catch (Throwable t) {   t.printStackTrace();  } } @Override  public void onFailure(Request request, IOException e) {  callFailure(e); } @Override   public void onResponse(okhttp3.Response rawResponse) {  Response<T> response;  try {   //解析結果   response = parseResponse(rawResponse);  } catch (Throwable e) {   callFailure(e);   return;  }  callSuccess(response); }});}

如此一來,OkHttpClient的回調也被引導到我們的Callback上來,整個流程就已經走通了。

總結
終於到了總結的時候了,一般來說,這都是乾貨的時候,哈哈~

Volley對比優勢
1.緩衝處理;Volley自己就提供了一套完整的緩衝處理方案,預設使用檔案儲存體到磁碟中,並且提供了TTL SOFTTTL這麼體貼入微的機制;一個Request可能存在兩個Response,對於需要顯示緩衝,再顯示網路資料的情境真是爽的不要不要的。而Retrofit中並沒有提供任何和緩衝相關的方案。
2.代碼簡單,可讀性高。Volley的代碼是寫的如此的直接了當,讓你看起代碼來都不需要喝口茶,這樣的好處是我們我們需要修改代碼時不太容易引入bug....囧
同一個請求如果同時都在發送,那麼實際上只會有一個請求真正發出去, 其它的請求會等待這個結果回來,算小小最佳化吧。實際上這種情境不是很多哈,如果有,可能是你代碼有問題...
請求發送的時候提供了優先順序的概念,但是是只保證順序出去,不保證順序回來,然並卵。
支援不同的Http用戶端實現,預設提供了HttpClient和HttpUrlConnection的實現,而Retrofit在2.0版本之後,鎖死在OkHttp上了。
3.支援要求取消
Retrofit
1.發送請求真簡單,定義一個方法就可以了,這麼簡單的請求架構還有誰?Volley?
2.較好的可擴充性,Volley中每一個建立一個Request時都需要指定一個父類,告知序列化資料的方式,而Retrofit中只需要在配置時,指定各種轉換器即可。CallAdapter的存在,可以使你隨意代理調用的Call,不錯不錯。。。
3.OkHttpClient內建並發光環,而Volley中的背景工作執行緒是自己維護的,那麼就有可能存線上程由於異常退出之後,沒有下一個背景工作執行緒補充的風險(線程池可以彌補這個缺陷),這在Retrofit中不存在,因為即使有問題,這個鍋也會甩給OkHttp,嘿嘿
4.支援要求取消

再次說明的是,Retrofit沒有對緩衝提供任何額外支援,也就是說它只能通過HTTP的Cache control做檔案儲存體,這樣就會有一些問題:
1.需要Server通過Cache control頭部來控制緩衝,需要修改後台代碼
2.有些地方比較適合使用資料庫來儲存,比如關係儲存,此時,Retrofit就無能為力了
3.緩衝不在我們的控制範圍之內,而是完全通過OkHttp來管理,多少有些不便,比如我們要刪除某一個指定的緩衝,或者更新某一個指定緩衝,代碼寫起來很彆扭(自己hack要求標頭裡面的cache contrl)

而在我們項目的實際使用過程中,緩衝是一個比較重要的角色,Retrofit對緩衝的支援度不是很好,真是讓人傷心。。。
但是,我們還是覺得在使用中Retrofit真心比較方便,容易上手,通過註解代碼可讀性和可維護性提升了N個檔次,幾乎沒有樣板代碼(好吧,如果你覺得每個請求都需要定義一個方法,那這也算。。),所以最後的決定是選擇Retrofit。

有人說了,Volley中的兩次響應和緩衝用起來很happy怎麼辦?嗯,我們會修改Retrofit,使它支援檔案儲存體和ORM儲存,並將Volley的緩衝 網路兩次響應回調移接過來,這個項目正在測試階段,待我們項目做完小白鼠,上線穩定之後,我會把代碼開源,大家敬請關注。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.