Retrofit 2.0:有史以來最大的改進

不熟悉Retrofit的同學可以先參考這篇文章:Retrofit – Java(Android) 的REST 接口封裝類庫谴供,很適合入門颗味。

因為其簡單與出色的性能,Retrofit 是安卓上最流行的HTTP Client庫之一宇色。

不過它的缺點是在Retrofit 1.x中沒有直接取消正在進行中任務的方法讶请。如果你想做這件事必須手動殺死鸵贬,而這并不好實現(xiàn)俗他。

Square幾年前曾許諾這個功能將在Retrofit 2.0實現(xiàn),但是幾年過去了仍然沒有在這個問題上有所更新阔逼。

直到上周兆衅,Retrofit 2.0 才從候選發(fā)布階段變成Beta 1?,并且公開給所有人嗜浮。在嘗試了之后羡亩,我不得不說自己對新的模式和新的功能印象深刻。有許多改進危融,本文將討論它們畏铆。讓我們開始吧!

包還是那個包只是換了新版本

如果你想在自己的項目中導入Retrofit 2.0吉殃,那么在build.gradle的依賴一節(jié)里面添加這行代碼:

compile'com.squareup.retrofit:retrofit:2.0.0-beta1'

Sync gradle 文件之后你就可以使用Retrofit 2.0了辞居。

新的Service定義方式,不再有同步和異步之分

關于在Retrofit 1.9中service 接口的定義蛋勺,如果你想定義一個同步的函數(shù)瓦灶,你應該這樣定義:

```

publicclassPair{publicObject first;publicObject second;publicPair(){ first =newObject(); second =newObject(); }publicstaticPairmake(Object fir, Object sec){ Pair p =newPair(); p.first = fir; p.second = sec;returnp; }}

```

publicclassPair{publicObject first;publicObject second;publicPair(){ first =newObject(); second =newObject(); }publicstaticPairmake(Object fir, Object sec){ Pair p =newPair(); p.first = fir; p.second = sec;returnp; }}

作者:斯云

鏈接:http://www.reibang.com/p/be23f547fa67

來源:簡書

著作權歸作者所有。商業(yè)轉載請聯(lián)系作者獲得授權抱完,非商業(yè)轉載請注明出處贼陶。

/*?Synchronous?in?Retrofit?1.9?*/

publicinterfaceAPIService{

@POST("/list")

RepoloadRepo();

}

而定義一個異步的則是這樣:

/*?Asynchronous?in?Retrofit?1.9?*/

publicinterfaceAPIService{

@POST("/list")

voidloadRepo(Callbackcb);

}

但是在Retrofit 2.0上,只能定義一個模式巧娱,因此要簡單得多碉怔。

importretrofit.Call;

/*?Retrofit?2.0?*/

publicinterfaceAPIService{

@POST("/list")

CallloadRepo();

}

而創(chuàng)建service 的方法也變得和OkHttp的模式一模一樣。如果要調用同步請求禁添,只需調用execute眨层;而發(fā)起一個異步請求則是調用enqueue。

同步請求

//?Synchronous?Call?in?Retrofit?2.0

Callcall=service.loadRepo();

Reporepo=call.execute();

以上的代碼會阻塞線程上荡,因此你不能在安卓的主線程中調用趴樱,不然會面臨NetworkOnMainThreadException。如果你想調用execute方法酪捡,請在后臺線程執(zhí)行叁征。

異步請求

//?Synchronous?Call?in?Retrofit?2.0

Callcall=service.loadRepo();

call.enqueue(newCallback(){

@Override

publicvoidonResponse(Responseresponse){

//?Get?result?Repo?from?response.body()

}

@Override

publicvoidonFailure(Throwablet){

}

});

以上代碼發(fā)起了一個在后臺線程的請求并從response 的response.body()方法中獲取一個結果對象。注意這里的onResponse和onFailure方法是在主線程中調用的逛薇。

我建議你使用enqueue捺疼,它最符合 Android OS的習慣。

取消正在進行中的業(yè)務

service 的模式變成Call的形式的原因是為了讓正在進行的事務可以被取消永罚。要做到這點啤呼,你只需調用call.cancel()卧秘。

call.cancel();

事務將會在之后立即被取消。好簡單嘿嘿官扣!

Converter現(xiàn)在從Retrofit中刪除

在Retrofit 1.9中翅敌,GsonConverter 包含在了package 中而且自動在RestAdapter創(chuàng)建的時候被初始化。這樣來自服務器的son結果會自動解析成定義好了的Data Access Object(DAO)

但是在Retrofit 2.0中惕蹄,Converter 不再包含在package 中了蚯涮。你需要自己插入一個Converter 不然的話Retrofit 只能接收字符串結果。同樣的卖陵,Retrofit 2.0也不再依賴于Gson 遭顶。

如果你想接收json 結果并解析成DAO,你必須把Gson Converter 作為一個獨立的依賴添加進來泪蔫。

compile'com.squareup.retrofit:converter-gson:2.0.0-beta1'

然后使用addConverterFactory把它添加進來棒旗。注意RestAdapter的別名仍然為Retrofit。

Retrofitretrofit=newRetrofit.Builder()

.baseUrl("http://api.nuuneoi.com/base/")

.addConverterFactory(GsonConverterFactory.create())

.build();

service=retrofit.create(APIService.class);

這里是Square提供的官方Converter modules列表撩荣。選擇一個最滿足你需求的嗦哆。

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)建一個自定義的converter 。

我比較贊同這種新的模式婿滓。它讓Retrofit對自己要做的事情看起來更清晰老速。

自定義Gson對象

為了以防你需要調整json里面的一些格式,比如凸主,Date Format橘券。你可以創(chuàng)建一個Gson 對象并把它傳遞給GsonConverterFactory.create()。

Gsongson=newGsonBuilder()

.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")

.create();

Retrofitretrofit=newRetrofit.Builder()

.baseUrl("http://api.nuuneoi.com/base/")

.addConverterFactory(GsonConverterFactory.create(gson))

.build();

service=retrofit.create(APIService.class);

完成卿吐。

新的URL定義方式

Retrofit 2.0使用了新的URL定義方式旁舰。Base URL與@Url 不是簡單的組合在一起而是和的處理方式一致。用下面的幾個例子闡明嗡官。

ps:貌似第二個才符合習慣箭窜。

對于 Retrofit 2.0中新的URL定義方式,這里是我的建議:

-?Base URL: 總是以?/結尾

-?@Url:?不要以 / 開頭

比如

publicinterfaceAPIService{

@POST("user/list")

CallloadUsers();

}

publicvoiddoSomething(){

Retrofitretrofit=newRetrofit.Builder()

.baseUrl("http://api.nuuneoi.com/base/")

.addConverterFactory(GsonConverterFactory.create())

.build();

APIServiceservice=retrofit.create(APIService.class);

}

以上代碼中的loadUsers會從http://api.nuuneoi.com/base/user/list獲取數(shù)據(jù)衍腥。

而且在Retrofit 2.0中我們還可以在@Url里面定義完整的URL:

publicinterfaceAPIService{

@POST("http://api.nuuneoi.com/special/user/list")

CallloadSpecialUsers();

}

這種情況下Base URL會被忽略磺樱。

可以看到在URL的處理方式上發(fā)生了很大變化。它和前面的版本完全不同婆咸。如果你想把代碼遷移到Retrofit 2.0竹捉,別忘了修正URL部分的代碼。

現(xiàn)在需要OkHttp的支持

OkHttp 在Retrofit 1.9里是可選的尚骄。如果你想讓Retrofit 使用OkHttp 作為HTTP 連接接口块差,你需要手動包含okhttp?依賴。

但是在Retrofit 2.0中,OkHttp 是必須的憨闰,并且自動設置為了依賴状蜗。下面的代碼是從Retrofit 2.0的pom文件中抓取的。你不需要再做任何事情了鹉动。

com.squareup.okhttp

okhttp

...

為了讓OkHttp 的Call模式成為可能轧坎,在Retrofit 2.0中OkHttp 自動被用作HTTP 接口。

即使response存在問題onResponse依然被調用

在Retrofit 1.9中训裆,如果獲取的 response 不能背解析成定義好的對象,則會調用failure蜀铲。但是在Retrofit 2.0中边琉,不管 response 是否能被解析。onResponse總是會被調用记劝。但是在結果不能被解析的情況下变姨,response.body()會返回null。別忘了處理這種情況厌丑。

如果response存在什么問題定欧,比如404什么的,onResponse也會被調用怒竿。你可以從response.errorBody().string()中獲取錯誤信息的主體砍鸠。

Response/Failure 邏輯和Retrofit 1.9差別很大。如果你決定遷移到Retrofit 2.0耕驰,注意小心謹慎的處理這些情況爷辱。

缺少INTERNET權限會導致SecurityException異常

在Retrofit 1.9中,如果你忘記在AndroidManifest.xml文件中添加INTERNET權限朦肘。異步請求會直接進入failure回調方法饭弓,得到PERMISSION DENIED?錯誤消息。沒有任何異常被拋出媒抠。

但是在Retrofit 2.0中弟断,當你調用call.enqueue或者call.execute,將立即拋出SecurityException趴生,如果你不使用try-catch會導致崩潰阀趴。

這類似于在手動調用HttpURLConnection時候的行為。不過這不是什么大問題苍匆,因為當INTERNET權限添加到了 AndroidManifest.xml中就沒有什么需要考慮的了舍咖。

Use an?Interceptor from?OkHttp

在Retrofit 1.9中,你可以使用RequestInterceptor來攔截一個請求锉桑,但是它已經(jīng)從Retrofit 2.0 移除了排霉,因為HTTP連接層已經(jīng)轉為OkHttp。

結果就是,現(xiàn)在我們必須轉而使用OkHttp里面的Interceptor攻柠。首先你需要使用Interceptor創(chuàng)建一個OkHttpClient對象球订,如下:

OkHttpClientclient=newOkHttpClient();

client.interceptors().add(newInterceptor(){

@Override

publicResponseintercept(Chainchain)throwsIOException{

Responseresponse=chain.proceed(chain.request());

//?Do?anything?with?response?here

returnresponse;

}

});

然后傳遞創(chuàng)建的client到Retrofit的Builder鏈中。

Retrofitretrofit=newRetrofit.Builder()

.baseUrl("http://api.nuuneoi.com/base/")

.addConverterFactory(GsonConverterFactory.create())

.client(client)

.build();

以上為全部內容瑰钮。

學習關于OkHttp Interceptor的知識冒滩,請到OkHttp Interceptors

RxJava Integration with CallAdapter

除了使用Call模式來定義接口浪谴,我們也可以定義自己的type开睡,比如MyCall。苟耻。我們把Retrofit 2.0的這個機制稱為CallAdapter篇恒。

Retrofit團隊有已經(jīng)準備好了的CallAdapter module。其中最著名的module可能是為RxJava準備的CallAdapter凶杖,它將作為Observable返回胁艰。要使用它,你的項目依賴中必須包含兩個modules智蝠。

compile'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'

compile'io.reactivex:rxandroid:1.0.1'

Sync Gradle并在Retrofit Builder鏈表中如下調用addCallAdapterFactory:

Retrofitretrofit=newRetrofit.Builder()

.baseUrl("http://api.nuuneoi.com/base/")

.addConverterFactory(GsonConverterFactory.create())

.addCallAdapterFactory(RxJavaCallAdapterFactory.create())

.build();

你的Service接口現(xiàn)在可以作為Observable返回了腾么!

Retrofitretrofit=newRetrofit.Builder()

.baseUrl("http://api.nuuneoi.com/base/")

.addConverterFactory(GsonConverterFactory.create())

.addCallAdapterFactory(RxJavaCallAdapterFactory.create())

.build();

你可以完全像RxJava那樣使用它,如果你想讓subscribe部分的代碼在主線程被調用杈湾,需要把observeOn(AndroidSchedulers.mainThread())添加到鏈表中解虱。

Observableobservable=service.loadDessertListRx();

observable.observeOn(AndroidSchedulers.mainThread())

.subscribe(newSubscriber(){

@Override

publicvoidonCompleted(){

Toast.makeText(getApplicationContext(),

"Completed",

Toast.LENGTH_SHORT)

.show();

}

@Override

publicvoidonError(Throwablee){

Toast.makeText(getApplicationContext(),

e.getMessage(),

Toast.LENGTH_SHORT)

.show();

}

@Override

publicvoidonNext(DessertItemCollectionDaodessertItemCollectionDao){

Toast.makeText(getApplicationContext(),

dessertItemCollectionDao.getData().get(0).getName(),

Toast.LENGTH_SHORT)

.show();

}

});

完成!我相信RxJava的粉絲對這個變化相當滿意漆撞。

總結

還有許多其他變化饭寺,你可以在官方的Change Log中獲取更多詳情。不過叫挟,我相信我已經(jīng)在本文涵蓋了主要的issues艰匙。

你可能會好奇現(xiàn)在是否是切換到Retrofit 2.0 的時機?考慮到它仍然是beta階段抹恳,你可能會希望繼續(xù)停留在1.9除非你跟我一樣是一個喜歡嘗鮮的人员凝。?Retrofit 2.0用起來很好據(jù)我的經(jīng)驗來看還沒有發(fā)現(xiàn)bug。

注意Retrofit 1.9 的官方文檔現(xiàn)在已經(jīng)從Square的github主頁刪除奋献。我建議你現(xiàn)在就開始學習Retrofit 2.0健霹,盡快使用最新版本。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末瓶蚂,一起剝皮案震驚了整個濱河市糖埋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌窃这,老刑警劉巖瞳别,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡祟敛,警方通過查閱死者的電腦和手機疤坝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來馆铁,“玉大人跑揉,你說我怎么就攤上這事〔壕蓿” “怎么了历谍?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長辣垒。 經(jīng)常有香客問我望侈,道長,這世上最難降的妖魔是什么乍构? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任圾另,我火速辦了婚禮炮叶,結果婚禮上,老公的妹妹穿的比我還像新娘美澳。我一直安慰自己陵究,他們只是感情好眠饮,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著铜邮,像睡著了一般仪召。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上松蒜,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天扔茅,我揣著相機與錄音,去河邊找鬼秸苗。 笑死召娜,一個胖子當著我的面吹牛,可吹牛的內容都是我干的惊楼。 我是一名探鬼主播玖瘸,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼檀咙!你這毒婦竟也來了雅倒?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤弧可,失蹤者是張志新(化名)和其女友劉穎蔑匣,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡殖演,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年氧秘,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片趴久。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡丸相,死狀恐怖,靈堂內的尸體忽然破棺而出彼棍,到底是詐尸還是另有隱情灭忠,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布座硕,位于F島的核電站弛作,受9級特大地震影響,放射性物質發(fā)生泄漏华匾。R本人自食惡果不足惜映琳,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蜘拉。 院中可真熱鬧萨西,春花似錦、人聲如沸旭旭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽持寄。三九已至源梭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間稍味,已是汗流浹背废麻。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留模庐,地道東北人烛愧。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像赖欣,于是被迫代替她去往敵國和親屑彻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內容