亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

簡略分析Android的Retrofit應(yīng)用開發(fā)框架源碼

 更新時(shí)間:2016年02月19日 16:41:59   作者:楚云之南  
這篇文章主要介紹了Android的Retrofit應(yīng)用開發(fā)框架的源碼分析,作者對Volley和Retrofit兩個(gè)框架進(jìn)行了一些對比,比較精彩,需要的朋友可以參考下

面對一個(gè)項(xiàng)目,對于Android應(yīng)用開發(fā)框架的選擇,我想過三種方案:
1.使用Loader + HttpClient + GreenDao + Gson + Fragment,優(yōu)點(diǎn)是可定制性強(qiáng),由于使用Google家自己的Loader和LoaderManager,代碼健壯性強(qiáng)。
缺點(diǎn)是整套代碼學(xué)習(xí)成本較高,使用過程中樣板代碼較多,(比如每一個(gè)Request都需要產(chǎn)生一個(gè)新類)
2.Volley,作為Google在IO大會上得瑟過的一個(gè)網(wǎng)絡(luò)庫,其實(shí)不算什么新東西(2013 IO發(fā)布),使用較為簡單,請求可以取消,可以提供優(yōu)先級請求,看起來還是不錯(cuò)的。
3.Retrofit,一款為了使請求極度簡單化的REST API Client,呼聲也很高,使用門檻幾乎是小白型。
如何選擇呢?首先干掉1,因?yàn)閷π氯说膶W(xué)習(xí)成本確實(shí)太高,如果要快速開發(fā)一個(gè)項(xiàng)目,高學(xué)習(xí)成本是致命的,同時(shí)使用起來樣板代碼很多。

那么如何在Volley和Retrofit中選擇呢?盡管網(wǎng)上有很多文章在介紹兩個(gè)框架的使用方法,而對于其原理,特別是對比分析較少,如果你手里有一個(gè)項(xiàng)目,如何選擇網(wǎng)絡(luò)模塊呢?
首先說明一下這兩個(gè)網(wǎng)絡(luò)框架在項(xiàng)目中的層次:

2016219163706184.png (716×343)

從上圖可知,不管Volley還是Retrofit,它們都是對現(xiàn)有各種方案進(jìn)行整合,并提供一個(gè)友好,快速開發(fā)的方案,在整合過程中,各個(gè)模塊都可以自行定制 或者替換。比如反序列化的工作,再比如HttpClient。

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


注意,本文并不是使用Retrofit的幫助文檔,建議先看Retrofit的文檔和OkHttp的文檔,這些對于理解余下部分很重要。

使用Retrofit發(fā)送一個(gè)請求
假設(shè)我們要從這個(gè)地址 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來解析結(jié)果

4.新建請求接口
Retrofit使用注解來定義一個(gè)請求,在方法上面指定請求的方法等信息,在參數(shù)中指定參數(shù)等信息。

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.發(fā)送請求,我們可以發(fā)送同步請求(阻塞當(dāng)前線程)和異步請求,并在回調(diào)中處理請求結(jié)果。

 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整個(gè)項(xiàng)目中使用了動態(tài)代理和靜態(tài)代理,如果你不太清楚代理模式,建議先google一下,如果對于Java的動態(tài)代理原理不是太熟悉,強(qiáng)烈建議先看:這篇文章-戲說代理和Java動態(tài)代理
ok,下面按照我們使用Retrofit發(fā)送請求的步驟來:

RestApi restApi = retrofit.create(RestApi.class);  //產(chǎn)生一個(gè)RestApi的實(shí)例

輸入一個(gè)接口,直接輸出一個(gè)實(shí)例。

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

public <T> T create(final Class<T> service) {
 //返回一個(gè)動態(tài)代理類的實(shí)例
 return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
  //這個(gè)InvocationHandler是關(guān)鍵所在,以后調(diào)用restapi接口中的方法都會被發(fā)送到這里
  new InvocationHandler() {
   private final Platform platform = Platform.get();

   @Override 
   public Object invoke(Object proxy, Method method, Object... args)
     throws Throwable {
    /* 如果是Object的方法,如toString()等,直接調(diào)用InvocationHandler的方法,
     *注意,這里其實(shí)是沒有任何意義的,因?yàn)镮nvocationHandler其實(shí)是一個(gè)命令傳送者
     *在動態(tài)代理中,這些方法是沒有任何語義的,所以不需要在意
     */
    if (method.getDeclaringClass() == Object.class) {
     return method.invoke(this, args);
    }
    //對于Java8的兼容,在Android中不需要考慮
    if (platform.isDefaultMethod(method)) {
     return platform.invokeDefaultMethod(method, service, proxy, args);
    }
    //返回一個(gè)Call對象
    return loadMethodHandler(method).invoke(args);
   } 
  });
}

我們可以看到Retrofit.create()之后,返回了一個(gè)接口的動態(tài)代理類的實(shí)例,那么我們調(diào)用這個(gè)代理類的方法時(shí),調(diào)用自然就被發(fā)送到我們定義的InvocationHandler中,所以調(diào)用

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

時(shí),直接調(diào)用到InvocationHandler的invoke方法,下面是invoke此時(shí)的上下文:

  @Override 
   public Object invoke(Object proxy, Method method, Object... args)
     throws Throwable {
     //proxy對象就是你在外面調(diào)用方法的resetApi對象
     //method是RestApi中的函數(shù)定義,
     //據(jù)此,我們可以獲取定義在函數(shù)和參數(shù)上的注解,比如@GET和注解中的參數(shù)
     //args,實(shí)際參數(shù),這里傳送的就是字符串"retrofit"

    //這里必然是return 一個(gè)Call對象
    return loadMethodHandler(method).invoke(args);
   }

此時(shí),invoke的返回必然是一個(gè)Call,Call是Retrofit中對一個(gè)Request的抽象,由此,大家應(yīng)該不難想象到loadMethodHandler(method).invoke(args); 這句代碼應(yīng)該就是去解析接口中傳進(jìn)來的注解,并生成一個(gè)OkHttpClient中對應(yīng)的請求,這樣我們調(diào)用searchResultsCall時(shí),調(diào)用OkHttpClient走網(wǎng)絡(luò)即可。確實(shí),Retrofit的主旋律的確就是這樣滴。

注意,實(shí)際上Call,CallBack這種描述方式是在OkHttp中引入的,Retrofit底層使用OkHttp所以也是使用這兩個(gè)類名來抽象一個(gè)網(wǎng)絡(luò)請求和一個(gè)請求回來之后的回調(diào)??傮w來看,Retrofit中的Call Callback持有一個(gè)OkHttp的Call Callback,將對Retrofit中的各種調(diào)用轉(zhuǎn)發(fā)到OkHttp的類庫中,實(shí)際上這里就是靜態(tài)代理啦,因?yàn)槲覀儠x各種代理類,比如OkHttpCall

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

//這個(gè)OkHttp的工廠,用于產(chǎn)生一個(gè)OkHttp類庫中的Call,實(shí)際上就是傳入配置的Builder的OkHttpClient
private final okhttp3.Call.Factory callFactory;
//通過Method中的注解和傳入的參數(shù),組建一個(gè)OkHttp的Request
private final RequestFactory requestFactory;
//用于對Retrofit中的Call進(jìn)行代理
private final CallAdapter<?> callAdapter;
//用于反序列化返回結(jié)果
private final Converter<ResponseBody, ?> responseConverter;

// 返回一個(gè)Call對象
Object invoke(Object... args) { 
  return callAdapter.adapt(new OkHttpCall<>(callFactory, requestFactory, args, responseConverter));
}

在Retrofit中通過添加Converter.Factory來為Retrofit添加請求和響應(yīng)的數(shù)據(jù)編碼和解析。所以我們可以添加多個(gè)Converter.Factory為Retrofit提供處理不同數(shù)據(jù)的功能。

CallAdapter 可以對執(zhí)行的Call進(jìn)行代理,這里是靜態(tài)代理。我們也可以通過添加自己的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) {
    //查看是否有緩存,否則直接走網(wǎng)絡(luò)
    if(cached) {
      return cache;
    }
    this.delegate.enqueue(callback);
  }
}

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

@Override 
public void enqueue(final Callback<T> callback) {
 okhttp3.Call rawCall;
 try {
  //創(chuàng)建一個(gè)okhttp的Call
  rawCall = createRawCall();
 } catch (Throwable t) {
  callback.onFailure(t);
  return;
 }
 //直接調(diào)用okhttp的入隊(duì)操作
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 {
   //解析結(jié)果
   response = parseResponse(rawResponse);
  } catch (Throwable e) {
   callFailure(e);
   return;
  }
  callSuccess(response);
 }
});
}

如此一來,OkHttpClient的回調(diào)也被引導(dǎo)到我們的Callback上來,整個(gè)流程就已經(jīng)走通了。

總結(jié)
終于到了總結(jié)的時(shí)候了,一般來說,這都是干貨的時(shí)候,哈哈~

Volley對比優(yōu)勢
1.緩存處理;Volley自己就提供了一套完整的緩存處理方案,默認(rèn)使用文件存儲到磁盤中,并且提供了TTL SOFTTTL這么體貼入微的機(jī)制;一個(gè)Request可能存在兩個(gè)Response,對于需要顯示緩存,再顯示網(wǎng)絡(luò)數(shù)據(jù)的場景真是爽的不要不要的。而Retrofit中并沒有提供任何和緩存相關(guān)的方案。
2.代碼簡單,可讀性高。Volley的代碼是寫的如此的直接了當(dāng),讓你看起代碼來都不需要喝口茶,這樣的好處是我們我們需要修改代碼時(shí)不太容易引入bug....囧
同一個(gè)請求如果同時(shí)都在發(fā)送,那么實(shí)際上只會有一個(gè)請求真正發(fā)出去, 其它的請求會等待這個(gè)結(jié)果回來,算小小優(yōu)化吧。實(shí)際上這種場景不是很多哈,如果有,可能是你代碼有問題...
請求發(fā)送的時(shí)候提供了優(yōu)先級的概念,但是是只保證順序出去,不保證順序回來,然并卵。
支持不同的Http客戶端實(shí)現(xiàn),默認(rèn)提供了HttpClient和HttpUrlConnection的實(shí)現(xiàn),而Retrofit在2.0版本之后,鎖死在OkHttp上了。
3.支持請求取消
Retrofit
1.發(fā)送請求真簡單,定義一個(gè)方法就可以了,這么簡單的請求框架還有誰?Volley?
2.較好的可擴(kuò)展性,Volley中每一個(gè)新建一個(gè)Request時(shí)都需要指定一個(gè)父類,告知序列化數(shù)據(jù)的方式,而Retrofit中只需要在配置時(shí),指定各種轉(zhuǎn)換器即可。CallAdapter的存在,可以使你隨意代理調(diào)用的Call,不錯(cuò)不錯(cuò)。。。
3.OkHttpClient自帶并發(fā)光環(huán),而Volley中的工作線程是自己維護(hù)的,那么就有可能存在線程由于異常退出之后,沒有下一個(gè)工作線程補(bǔ)充的風(fēng)險(xiǎn)(線程池可以彌補(bǔ)這個(gè)缺陷),這在Retrofit中不存在,因?yàn)榧词褂袉栴},這個(gè)鍋也會甩給OkHttp,嘿嘿
4.支持請求取消

再次說明的是,Retrofit沒有對緩存提供任何額外支持,也就是說它只能通過HTTP的Cache control做文件存儲,這樣就會有一些問題:
1.需要Server通過Cache control頭部來控制緩存,需要修改后臺代碼
2.有些地方比較適合使用數(shù)據(jù)庫來存儲,比如關(guān)系存儲,此時(shí),Retrofit就無能為力了
3.緩存不在我們的控制范圍之內(nèi),而是完全通過OkHttp來管理,多少有些不便,比如我們要?jiǎng)h除某一個(gè)指定的緩存,或者更新某一個(gè)指定緩存,代碼寫起來很別扭(自己hack請求頭里面的cache contrl)

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

有人說了,Volley中的兩次響應(yīng)和緩存用起來很happy怎么辦?嗯,我們會修改Retrofit,使它支持文件存儲和ORM存儲,并將Volley的緩存 網(wǎng)絡(luò)兩次響應(yīng)回調(diào)移接過來,這個(gè)項(xiàng)目正在測試階段,待我們項(xiàng)目做完小白鼠,上線穩(wěn)定之后,我會把代碼開源,大家敬請關(guān)注。

相關(guān)文章

最新評論