一篇文章讀懂Retrofit2.0湖笨,Retrofit2.0更新分析

[toc]

該文章為,國外博客的翻譯蹦骑,英文比較爛(主要靠Google)慈省,可能有很多翻譯得不到位的地方,主要還是想給自己做一個筆記眠菇。

原文連接
https://inthecheesefactory.com/blog/retrofit-2.0/en

由于其簡單性和與其他網(wǎng)絡(luò)框架相比的卓越性能边败,Retrofit是Android的最流行的HTTP客戶端庫之一。

無論如何捎废,它的弱點是在Retrofit 1.x中笑窜,沒有任何直接的方式取消正在進(jìn)行的網(wǎng)絡(luò)請求。如果你想這樣做登疗,你必須在Thread上調(diào)用它排截,并手動殺死該Thread,這是一種很難管理的方式辐益。

Square幾年前承諾断傲,此功能將在Retrofit 2.0上推出,但過去幾年智政,仍然沒有更新的新聞认罩。

直到上周(譯者注:該博客發(fā)表時間為 2015-11-06 12:33),Retrofit 2.0才剛剛通過了Beta 1的發(fā)布階段续捂,并已被公開發(fā)布給大家垦垂。嘗試之后,我必須說疾忍,我對它的新模式和新功能印象深刻乔外。有很多往好的方向的變化。我將在本文中描述一罩。讓我們開始吧 杨幼!

一、新版本導(dǎo)入

如果要將Retrofit 2.0導(dǎo)入到項目中,請將此行添加到你 build.gradledependencies部分差购。

compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'

截至2017-10-3 最新版為:
compile 'com.squareup.retrofit2:retrofit:2.3.0'

同步你的 gradle 文件四瘫,你現(xiàn)在就可以使用Retrofit 2.0了 =)

正如你所看到的,Retrofit 2包名稱與以前的版本不同∮樱現(xiàn)在是 com.squareup.retrofit2

二找蜜、新的Service聲明方式,同步和異步方法不再被區(qū)分稳析。

關(guān)于Retrofit 1.9中的Service接口聲明洗做,如果要聲明一個同步請求,則必須像這樣聲明:

/* Synchronous in Retrofit 1.9 */

public interface APIService {
      @POST("/list") 
      Repo loadRepo();
}

如果你需要一個異步請求彰居,如下所示:

/* Asynchronous in Retrofit 1.9 */

public interface APIService {
      @POST("/list")
      void loadRepo(Callback<Repo> cb);
}

但是在Retrofit 2.0中诚纸,它更簡單,因為你只能使用單個模式進(jìn)行聲明陈惰。

import retrofit.Call;

/* Retrofit 2.0 */

public interface APIService {
      @POST("/list")
      Call<Repo> loadRepo();
}

調(diào)用創(chuàng)建Service的方式也改變?yōu)榕cOkHttp相同的模式畦徘。
要調(diào)用是同步請求,只需調(diào)用execute
要調(diào)用是同步請求抬闯,只需調(diào)用enqueue

同步請求

// Synchronous Call in Retrofit 2.0
Call<Repo> call = service.loadRepo();
Repo repo = call.execute();

上面的源代碼將阻塞線程井辆,所以你不能在Android的主線程中調(diào)用它,否則你將面臨NetworkOnMainThreadException溶握。如果你想調(diào)用execute方法杯缺,你必須在后臺線程上執(zhí)行。

異步請求

// Asynchronous Call in Retrofit 2.0

Call<Repo> call = service.loadRepo();
call.enqueue(new Callback<Repo>() { 
      @Override
      public void onResponse(Response<Repo> response) {
            // Get result Repo from response.body()
      }

      @Override
      public void onFailure(Throwable t) {

      }
});

上述代碼將在后臺線程中發(fā)出請求奈虾,并將結(jié)果以O(shè)bject的形式取回夺谁,你可以通過response.body()方法從response中提取結(jié)果。

請注意:onResponseonFailure將會在主線程中調(diào)用肉微。

我建議你使用enqueue匾鸥,因為它最適合Android。

三碉纳、取消正在進(jìn)行的事務(wù)

Service聲明方式轉(zhuǎn)變Call的原因勿负,是使得正在進(jìn)行的事務(wù)能夠被取消。若要取消事務(wù)劳曹,只需調(diào)用call.cancel()

call.cancel();

調(diào)用該方法之后奴愉,事務(wù)將會很快的被取消。很輕松吧铁孵?= D

四锭硼、在新Service的構(gòu)建中,轉(zhuǎn)換器(Converter)現(xiàn)在被從Retrofit包中去除

在Retrofit 1.9中蜕劝,GsonConverter包含在包中檀头,并在RestAdapter創(chuàng)建時自動初始化轰异。因此,服務(wù)器返回的json將自動解析為定義的 數(shù)據(jù)訪問對象 (DAO)暑始。

但在Retrofit 2.0中搭独,轉(zhuǎn)換器(Converter)不再包含在包中。你需要自己添加一個轉(zhuǎn)換器(Converter)廊镜,否則Retrofit將只能接受String的結(jié)果牙肝。因此,Retrofit 2.0不再依賴于Gson嗤朴。

如果要接受json結(jié)果并將其解析成DAO配椭,則必須將Gson Converter作為單獨的依賴項。

compile 'com.squareup.retrofit2:converter-gson:2.3.0'

并通過addConverterFactory將其添加雹姊。請注意颂郎,RestAdapter現(xiàn)在也重命名為Retrofit

Retrofit retrofit = new Retrofit.Builder()
      .baseUrl("http://api.nuuneoi.com/base/")
      .addConverterFactory(GsonConverterFactory.create())
      .build();

service = retrofit.create(APIService.class);

以下是Square提供的官方轉(zhuǎn)換器(Converter)模塊列表容为。可以從中選擇一個最適合你要求的寺酪。

Gson: com.squareup.retrofit:converter-gson
Jackson: com.squareup.retrofit:converter-jackson
Moshi: com.squareup.retrofit:converter-moshi
Protobuf: com.squareup.retrofit:converter-protobuf
Wire: com.squareup.retrofit:converter-wire
Simple XML: com.squareup.retrofit:converter-simplexml

你還可以通過實現(xiàn)Converter.Factory接口坎背,創(chuàng)建一個自定義轉(zhuǎn)換器(Converter)。

我支持這種新模式寄雀。它使得Retrofit更專注于處理網(wǎng)絡(luò)連接(原文:It makes Retrofit more clear what it actually does.)得滤。

五、自定義Gson對象

如果你需要在json中調(diào)整某些格式盒犹,例如Date Format懂更。你可以通過創(chuàng)建一個Gson對象,并將其作為參數(shù)傳遞給 GsonConverterFactory.create()

        Gson gson = new GsonBuilder()
                .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
                .create();

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://api.nuuneoi.com/base/")
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();

        service = retrofit.create(APIService.class);

完成急膀。

六沮协、新的URL解析方式。與<a href>的相同

Retrofit 2.0附帶了新的URL解析方式卓嫂。Base URL 和 @Url 不只是簡單地組合在一起慷暂,而是以和<a href="...">相同的方式進(jìn)行解析。

請查看下面的示例以進(jìn)行說明晨雳。




以下是我對Retrofit 2.0中新的URL聲明模式的建議:

  • Base URL:始終以 / 結(jié)尾

  • @Url不要/ 開始

例如

public interface APIService {
      
      @POST("user/list") 
      Call<Users> loadUsers();

}

public void doSomething() { 
      Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://api.nuuneoi.com/base/")
            .addConverterFactory(GsonConverterFactory.create()) 
            .build();

      APIService service = retrofit.create(APIService.class);
}

上面代碼中的loadUsers()將從http://api.nuuneoi.com/base/user/list獲取數(shù)據(jù)

此外行瑞,在Retrofit 2.0中,我們還可以在@Url中聲明一個完整的URL:

public interface APIService {

      @POST("http://api.nuuneoi.com/special/user/list")
      Call<Users> loadSpecialUsers();

}

在這種情況下餐禁,Base URL將被忽略血久。

可以看到,URL解析有重大變化帮非。它與以前的版本完全不同氧吐。如果要將現(xiàn)有代碼升級到Retrofit 2.0讹蘑,請不要忘記修復(fù)這些URL部分的代碼。

七副砍、OkHttp現(xiàn)在為必用的

OkHttp在“Retrofit 1.9”中為可選項衔肢。如果要讓Retrofit使用OkHttp作為HTTP連接接口,那么你必須自己手動將okhttp添加至依賴項豁翎。

但是在Retrofit 2.0中角骤,必須使用OkHttp,并且會自動添加為依賴心剥。下面的代碼是從Retrofit 2.0的pom文件中獲取的邦尊。你沒有必要做任何事情。

  <dependencies>
    <dependency>
      <groupId>com.squareup.okhttp</groupId>
      <artifactId>okhttp</artifactId>
    </dependency>

    ...
  </dependencies>

在Retrofit 2.0中自動使用Okhttp作為HTTP接口优烧,目的是使用OkHttp的Call方式蝉揍,將網(wǎng)絡(luò)請求變成上文中描述的那樣。

八畦娄、即使返回值有問題(主要指無法解析)又沾,onResponse仍然會被調(diào)用

在Retrofit 1.9中,如果獲取的返回值無法解析到定義好的對象之中熙卡,failure將被調(diào)用杖刷。但是在Retrofit 2.0中,無論返回值是否能夠解析驳癌,onResponse都會被調(diào)用滑燃。但是在結(jié)果無法解析為Object的情況下,response.body()將返回為null颓鲜。注意:不要忘記處理這一種情況表窘。

如果返回值有問題,例如404 Not Found甜滨。onResponse也將被調(diào)用乐严。您可以response.errorBody().string()中檢索錯誤信息

Response / Failure的邏輯與Retrofit 1.9完全不同。如果你決定將代碼升級到Retrofit 2.0艳吠,請小心處理這些情況麦备。

九、缺少INTERNET權(quán)限會導(dǎo)致拋出SecurityException異常

在Retrofit 1.9中昭娩,如果您忘記在AndroidManifest.xml文件中添加 INTERNET
權(quán)限凛篙。異步請求將立即進(jìn)入failure的回調(diào)函數(shù)中,并帶著錯誤信息:PERMISSION DENIED栏渺。不會額外拋出異常呛梆。

但在Retrofit2.0之中,當(dāng)你調(diào)用call.enqueue或者call.execute時磕诊,會立刻拋出SecurityException填物。如果你沒有使用try-catch來處理該情況纹腌,很可能導(dǎo)致應(yīng)用崩潰。

這種情況和滞磺,當(dāng)手動調(diào)用HttpURLConnection是一樣的
無論如何升薯,這個問題不是一件大事,因為只要把INTERNET權(quán)限添加到AndroidManifest.xml之中击困,這個問題就不存在了涎劈。

十、使用OkHttp的攔截器

在Retrofit 1.9中阅茶,您可以使用 RequestInterceptor攔截請求蛛枚,但是由于HTTP連接層已被移動到 OkHttp,所以在Retrofit 2.0上已經(jīng)不能這么做了脸哀。

因此蹦浦,我們必須使用 OkHttpInterceptor。首先撞蜂,你必須像這樣創(chuàng)建一個OkHttpClient對象并創(chuàng)建一個攔截器

OkHttpClient client = new OkHttpClient();
client.interceptors().add(new Interceptor() {
      @Override
      public Response intercept(Chain chain) throws IOException {
            Response response = chain.proceed(chain.request());
            
            // Do anything with response here

            return response; 
      }
});

并將創(chuàng)建好的client傳入Retrofit的Builder的鏈?zhǔn)秸{(diào)度之中盲镶。

Retrofit retrofit = new Retrofit.Builder()
      .baseUrl("http://api.nuuneoi.com/base/")
      .addConverterFactory(GsonConverterFactory.create())
      .client(client)
      .build();

That's all.

要了解OkHttp攔截器的更多信息,請瀏覽 OkHttp攔截器蝌诡。
附:感謝漢之風(fēng)云中文翻譯版

十一徒河、證書鎖定(Certificate Pinning)

相關(guān)知識
移動安全之Certificate Pinning

與攔截器一樣,如果要使連接應(yīng)用的證書鎖定送漠,也需要先創(chuàng)建OkHttpClient實例。以下是示例代碼段由蘑。
首先闽寡,定義具有證書鎖定信息的OkHttpClient實例:

OkHttpClient client = new OkHttpClient.Builder()
        .certificatePinner(new CertificatePinner.Builder()
                .add("publicobject.com", "sha1/DmxUShsZuNiqPQsX2Oi9uv2sCnw=")
                .add("publicobject.com", "sha1/SXxoaOSEzPC6BgGmxAt/EAcsajw=")
                .add("publicobject.com", "sha1/blhOM3W9V/bVQhsWAcLYwPU6n24=")
                .add("publicobject.com", "sha1/T5x9IXmcrQ7YuQxXnxoCmeeQ84c=")
                .build())
        .build();

在Retrofit的Builder的鏈?zhǔn)秸{(diào)度之中,傳入剛創(chuàng)建好的 OkhttpClient尼酿。

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://api.nuuneoi.com/base/")
        .addConverterFactory(GsonConverterFactory.create())
        .client(client)
        .build();

有關(guān)證書鎖定的sha1哈希算法的更多信息爷狈,Google會幫助你(必應(yīng)國際版也挺好用)

十二、RxJava與CallAdapter集成

除了以Call<T>的形式聲明接口之外裳擎,我們也可以聲明自己的類型涎永,例如 MyCall<T>。這些方式被稱為“ CallAdapter”鹿响,它可以在Retrofit 2.0上使用

有一些可以從Retrofit團(tuán)隊獲得的即用型CallAdapter模塊羡微。最受歡迎的模塊之一可能是RxJava的CallAdapter,它將返回 Observable<T>惶我。要使用它妈倔,必須添加以下兩個依賴項。

    compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'
    compile 'io.reactivex:rxandroid:1.0.1'

同步Gradle并添加 addCallAdapterFactory到Retrofit Builder的鏈?zhǔn)秸{(diào)度中绸贡,如下所示:

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://api.nuuneoi.com/base/")
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();

你的Service接口現(xiàn)在可以就返回 Observable<T>了盯蝴!

public interface APIService {

    @POST("list")
    Call<DessertItemCollectionDao> loadDessertList();

    @POST("list")
    Observable<DessertItemCollectionDao> loadDessertListRx();

}

您可以用和RxJava完全相同的方式來使用它毅哗。另外,如果要讓訂閱部分的代碼在主線程上調(diào)用捧挺,observeOn(AndroidSchedulers.mainThread()) 需要添加到Observable的鏈?zhǔn)秸{(diào)度中虑绵。

        Observable<DessertItemCollectionDao> observable = service.loadDessertListRx();

        observable.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .unsubscribeOn(Schedulers.io())
            .subscribe(new Subscriber<DessertItemCollectionDao>() {
                @Override
                public void onCompleted() {
                    Toast.makeText(getApplicationContext(),
                            "Completed",
                            Toast.LENGTH_SHORT)
                        .show();
                }

                @Override
                public void onError(Throwable e) {
                    Toast.makeText(getApplicationContext(),
                            e.getMessage(),
                            Toast.LENGTH_SHORT)
                        .show();
                }

                @Override
                public void onNext(DessertItemCollectionDao dessertItemCollectionDao) {
                    Toast.makeText(getApplicationContext(),
                            dessertItemCollectionDao.getData().get(0).getName(),
                            Toast.LENGTH_SHORT)
                        .show();
                }
            });

完成了!我相信RxJava的粉絲是非常滿意的這個變化的= D

十三闽烙、結(jié)論

還有一些其他變化翅睛,您可以查看更改日志 了解更多信息。

請注意鸣峭,Retrofit 1.9官方文檔已從Square github網(wǎng)站中刪除宏所。我建議你現(xiàn)在開始學(xué)習(xí)Retrofit 2.0,并考慮在不久的將來升級到最新版本摊溶。= D

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末爬骤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蝶涩,老刑警劉巖焕毫,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異坷剧,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)喊暖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進(jìn)店門惫企,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人陵叽,你說我怎么就攤上這事狞尔。” “怎么了巩掺?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵偏序,是天一觀的道長。 經(jīng)常有香客問我胖替,道長研儒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任独令,我火速辦了婚禮端朵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘燃箭。我一直安慰自己逸月,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布遍膜。 她就那樣靜靜地躺著碗硬,像睡著了一般瓤湘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上恩尾,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天弛说,我揣著相機(jī)與錄音,去河邊找鬼翰意。 笑死木人,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的冀偶。 我是一名探鬼主播醒第,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼进鸠!你這毒婦竟也來了稠曼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤客年,失蹤者是張志新(化名)和其女友劉穎霞幅,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體量瓜,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡司恳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了绍傲。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扔傅。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖烫饼,靈堂內(nèi)的尸體忽然破棺而出铅鲤,到底是詐尸還是另有隱情,我是刑警寧澤枫弟,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站鹏往,受9級特大地震影響淡诗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜伊履,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一韩容、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧唐瀑,春花似錦群凶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赠尾。三九已至,卻和暖如春毅弧,著一層夾襖步出監(jiān)牢的瞬間气嫁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工够坐, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留寸宵,地道東北人。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓元咙,卻偏偏與公主長得像梯影,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子庶香,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容