Android上最流行的HTTP Client庫,Retrofit 2.0:有史以來最大的改進

jopen 9年前發布 | 294K 次閱讀 Retrofit

原文: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="...">的處理方式一致。用下面的幾個例子闡明。

Android上最流行的HTTP Client庫,Retrofit 2.0:有史以來最大的改進

Android上最流行的HTTP Client庫,Retrofit 2.0:有史以來最大的改進

Android上最流行的HTTP Client庫,Retrofit 2.0:有史以來最大的改進

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()中獲取錯誤信息的主體。

Android上最流行的HTTP Client庫,Retrofit 2.0:有史以來最大的改進

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會導致崩潰。

Android上最流行的HTTP Client庫,Retrofit 2.0:有史以來最大的改進

這類似于在手動調用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


 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!