欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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

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

2016219163706184.png (716×343)

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

而在本文我們將簡(jiǎn)略地來(lái)看一下Retrofit的源碼部分。


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

使用Retrofit發(fā)送一個(gè)請(qǐng)求
假設(shè)我們要從這個(gè)地址 http://www.exm.com/search.json?key=retrofit中獲取如下Json返回:

 {
  "data": [
        {
         "title":"Retrofit使用簡(jiǎn)介",
         "desc":"Retrofit是一款面向Android和Java的HttpClient",
         "link":"http://www.exm.com/retrofit"
        },
        {
         "title":"Retrofit使用簡(jiǎn)介",
         "desc":"Retrofit是一款面向Android和Java的HttpClient",
         "link":"http://www.exm.com/retrofit"
        },
        {
         "title":"Retrofit使用簡(jiǎn)介",
         "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來(lái)解析結(jié)果

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

public interface RestApi {
    @GET("/search.json")
    Call<List<SearchResult>> search(
      @Query("key") String key
       );

      //可以定義其它請(qǐng)求
      @GET("/something.json")
      Call<SomeThing> dosomething(
          @Query("params") long params
          .......
          .......
       );

}

5.發(fā)送請(qǐng)求,我們可以發(fā)送同步請(qǐng)求(阻塞當(dāng)前線程)和異步請(qǐng)求,并在回調(diào)中處理請(qǐng)求結(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)目中使用了動(dòng)態(tài)代理和靜態(tài)代理,如果你不太清楚代理模式,建議先google一下,如果對(duì)于Java的動(dòng)態(tài)代理原理不是太熟悉,強(qiáng)烈建議先看:這篇文章-戲說(shuō)代理和Java動(dòng)態(tài)代理
ok,下面按照我們使用Retrofit發(fā)送請(qǐng)求的步驟來(lái):

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

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

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

public <T> T create(final Class<T> service) {
 //返回一個(gè)動(dòng)態(tài)代理類的實(shí)例
 return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
  //這個(gè)InvocationHandler是關(guān)鍵所在,以后調(diào)用restapi接口中的方法都會(huì)被發(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í)是沒(méi)有任何意義的,因?yàn)镮nvocationHandler其實(shí)是一個(gè)命令傳送者
     *在動(dòng)態(tài)代理中,這些方法是沒(méi)有任何語(yǔ)義的,所以不需要在意
     */
    if (method.getDeclaringClass() == Object.class) {
     return method.invoke(this, args);
    }
    //對(duì)于Java8的兼容,在Android中不需要考慮
    if (platform.isDefaultMethod(method)) {
     return platform.invokeDefaultMethod(method, service, proxy, args);
    }
    //返回一個(gè)Call對(duì)象
    return loadMethodHandler(method).invoke(args);
   } 
  });
}

我們可以看到Retrofit.create()之后,返回了一個(gè)接口的動(dòng)態(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對(duì)象就是你在外面調(diào)用方法的resetApi對(duì)象
     //method是RestApi中的函數(shù)定義,
     //據(jù)此,我們可以獲取定義在函數(shù)和參數(shù)上的注解,比如@GET和注解中的參數(shù)
     //args,實(shí)際參數(shù),這里傳送的就是字符串"retrofit"

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

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

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

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

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

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

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

CallAdapter 可以對(duì)執(zhí)行的Call進(jìn)行代理,這里是靜態(tài)代理。我們也可以通過(guò)添加自己的CallAdapter來(lái)作一些操作,比如為請(qǐng)求加上緩存:

 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);
  }
}

至此,我們?cè)谡{(diào)用resetApi.search("retrofit");時(shí),實(shí)際上調(diào)用的層層代理之后的OkHttpCall,它是MethodHandler中invoke的時(shí)候塞入的??纯碠kHttpCall中的代碼吧:

@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);
 }
});
}

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

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

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

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

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

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

相關(guān)文章

最新評(píng)論