[toc]
該文章為,國外博客的翻譯蹦骑,英文比較爛(主要靠Google)慈省,可能有很多翻譯得不到位的地方,主要還是想給自己做一個筆記眠菇。
由于其簡單性和與其他網(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.gradle
的dependencies
部分差购。
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é)果。
請注意:onResponse
和onFailure
將會在主線程中調(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)不能這么做了脸哀。
因此蹦浦,我們必須使用 OkHttp 的Interceptor
。首先撞蜂,你必須像這樣創(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