Android上最流行的HTTP Client庫,Retrofit 2.0:有史以來最大的改進
原文:Retrofit 2.0: The biggest update yet on the best HTTP Client Library for Android
不熟悉Retrofit的同學可以先參考這篇文章: Retrofit – Java(Android) 的REST 接口封裝類庫 ,很適合入門。
因為其簡單與出色的性能,Retrofit 是安卓上最流行的HTTP Client庫之一。
不過它的缺點是在Retrofit 1.x中沒有直接取消正在進行中任務的方法。如果你想做這件事必須手動殺死,而這并不好實現。
Square幾年前曾許諾這個功能將在Retrofit 2.0實現,但是幾年過去了仍然沒有在這個問題上有所更新。
直到上周,Retrofit 2.0 才從候選發布階段變成Beta 1 ,并且公開給所有人。在嘗試了之后,我不得不說自己對新的模式和新的功能印象深刻。有許多改進,本文將討論它們。讓我們開始吧!
包還是那個包只是換了新版本
如果你想在自己的項目中導入Retrofit 2.0,那么在build.gradle的依賴一節里面添加這行代碼:
compile 'com.squareup.retrofit:retrofit:2.0.0-beta1'
Sync gradle 文件之后你就可以使用Retrofit 2.0了。
新的Service定義方式,不再有同步和異步之分
關于在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上,只能定義一個模式,因此要簡單得多。
import retrofit.Call; /* Retrofit 2.0 */ public interface APIService { @POST("/list") Call<Repo> loadRepo(); }
而創建service 的方法也變得和OkHttp的模式一模一樣。如果要調用同步請求,只需調用enqueue,或者調用enqueue來發起一個異步請求。
同步請求
// Synchronous Call in Retrofit 2.0 Call<Repo> call = service.loadRepo(); Repo repo = call.execute();
以上的代碼會阻塞線程,因此你不能在安卓的主線程中調用,不然會面臨NetworkOnMainThreadException。如果你想調用execute方法,請在后臺線程執行。
異步請求
// Synchronous 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) { } });
以上代碼發起了一個在后臺線程的請求并從response 的response.body()方法中獲取一個結果對象。注意這里的onResponse和onFailure方法是在主線程中調用的。
我建議你使用enqueue,它最符合 Android OS的習慣。
取消正在進行中的業務
service 的模式變成Call的形式的原因是為了讓正在進行的事務可以被取消。要做到這點,你只需調用call.cancel()。
call.cancel();
事務將會在之后立即被取消。好簡單嘿嘿!
Converter現在從Retrofit中刪除
在Retrofit 1.9中,GsonConverter 包含在了package 中而且自動在RestAdapter創建的時候被初始化。這樣來自服務器的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。
Retrofit retrofit = new Retrofit.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
你也可以通過實現Converter.Factory接口來創建一個自定義的converter 。
我比較贊同這種新的模式。它讓Retrofit對自己要做的事情看起來更清晰。
自定義Gson對象
為了以防你需要調整json里面的一些格式,比如,Date Format。你可以創建一個Gson 對象并把它傳遞給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定義方式
Retrofit 2.0使用了新的URL定義方式。Base URL與@Url 不是簡單的組合在一起而是和<a href="...">的處理方式一致。用下面的幾個例子闡明。
ps:貌似第二個才符合習慣。
對于 Retrofit 2.0中新的URL定義方式,這里是我的建議:
- Base URL: 總是以 /結尾
- @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獲取數據。
而且在Retrofit 2.0中我們還可以在@Url里面定義完整的URL:
public interface APIService { @POST("http://api.nuuneoi.com/special/user/list") Call<Users> loadSpecialUsers(); }
這種情況下Base URL會被忽略。
可以看到在URL的處理方式上發生了很大變化。它和前面的版本完全不同。如果你想把代碼遷移到Retrofit 2.0,別忘了修正URL部分的代碼。
現在需要OkHttp的支持
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>
為了讓OkHttp 的Call模式成為可能,在Retrofit 2.0中OkHttp 自動被用作HTTP 接口。
se即使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來攔截一個請求,但是它已經從Retrofit 2.0 移除了,因為HTTP連接層已經轉為OkHttp。
結果就是,現在我們必須轉而實用OkHttp里面的Interceptor。首先你需要實用Interceptor創建一個OkHttpClient對象,如下:
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; } });
然后傳遞創建的client到Retrofit的Builder鏈中。
Retrofit retrofit = new Retrofit.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團隊有已經準備好了的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:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://api.nuuneoi.com/base/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build();
你的Service接口現在可以作為Observable返回了!
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://api.nuuneoi.com/base/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build();
你可以完全像RxJava那樣使用它,如果你想讓subscribe部分的代碼在主線程被調用,需要把observeOn(AndroidSchedulers.mainThread())添加到鏈表中。
Observable<DessertItemCollectionDao> observable = service.loadDessertListRx(); observable.observeOn(AndroidSchedulers.mainThread()) .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的粉絲對這個變化相當滿意。
總結
還有許多其他變化,你可以在官方的Change Log 中獲取更多詳情。不過,我相信我已經在本文涵蓋了主要的issues。
你可能會好奇現在是否是切換到Retrofit 2.0 的時機?考慮到它仍然是beta階段,你可能會希望繼續停留在1.9除非你跟我一樣是一個喜歡嘗鮮的人。 Retrofit 2.0用起來很好據我的經驗來看還沒有發現bug。
注意Retrofit 1.9 的官方文檔現在已經從Square的github主頁刪除。我建議你現在就開始學習Retrofit 2.0,盡快使用最新版本。
來自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0915/3460.html