Android開源網路框架Retrofit(入門篇)

weixin_34337265發表於2016-12-12

Restful風格介面基本成了業界主流,Retrofit框架也大火特火,最近專案中也決定更新換代,採用Retrofit。本著學習之餘,也對接下來的學習者有一些幫助,於是寫了本文,主要的內容是對官網內容的一個翻譯和補充解釋
plus:本文假設你對於基本的HTTP協議有所瞭解。

一、Retrofit簡介

Retrofit內部使用OKhttp來進行網路請求, 會把網路請求轉化為一個java介面,使用了編譯階段的註解提高開發效率。如下,在開發中把網路請求定義在一個專門的java介面中

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

通過Retrofit為此介面生成一個實現,程式碼如下

//定製OKHttpClient
OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .readTimeout(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
                .connectTimeout(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
                .writeTimeout(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
                .build();

//例項化 Retrofit
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
     .client(okHttpClient)
    .build();


//生成實現類
GitHubService service = retrofit.create(GitHubService.class);

通過生成的GitHubService,開發者可以使用同步或者非同步的方式去請求網路介面。

Call<List<Repo>> repos = service.listRepos("octocat");

Retrofit 使用註解來描述HTTP介面,提升開發效率,它有如下特性:

  • URL引數動態替換、動態位址列引數。
  • 物件能動態轉化成RequestBody,比如 JSON和protocol buffers。
  • 支援Multipart RequestBody(對這個不瞭解的,看下這篇博文
  • 支援檔案上傳。

上述特性,具體看下面的章節就明白了。

二、Retrofit的註解用法詳解

1. 網路介面的宣告

在Retrofit中,每個網路介面都必須有一個註解,網路介面上的註解表明了型別、Url訪問路徑。這裡有五個內建的註解: GET, POST, PUT, DELETE, and HEAD。如下示例,宣告瞭一個簡單的GET網路介面,其相對路徑是users/list。

@GET("users/list")

跟傳統一樣,你可以在路徑中加上引數。

@GET("users/list?sort=desc")

2. URL相關的註解

相對路徑中可以宣告動態引數,用 {str1} 程式碼 表示,在介面裡面通過 @Path("str1") 修飾相應用於動態替換的引數。

具體用法如下展示,其中groupId的值會動態替換掉訪問路徑中的{id}。

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId);

位址列引數也是可以使用 @Query("str")加上,效果就是在訪問URL後面動態加上 @Query("str")標識的引數

如下方法,最終的URL會是這樣:group/{id}/users/?sort=xxx

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);

如果是想加上很多個位址列引數,Retrofit提供了map的方式,map裡面的多個鍵值對會依次加入到URL後邊。

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);

3. Request Body 相關注解

上文已經提到,物件能動態轉化成RequestBody,具體是要在物件前使用 @Body註解。如下程式碼所示,User物件會被轉化成Body。

@POST("users/new")
Call<User> createUser(@Body User user);

The object will also be converted using a converter specified on the Retrofit instance. If no converter is added, only RequestBody can be used.

物件轉化為Body的方式是Retrofit預設提供的一個轉化器,你也可以自己建立一個轉換器,在Retrofit例項化的時候進行指定。

4. 表單和使用Multipart Body

Retrofit使用註解 @FormUrlEncoded 來表示使用表單,表單裡面相應的欄位在介面引數前使用 @Field("XXX")標識,括號裡面是key值。

@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);

Multipart Body直接使用 @Multipart 在介面方法上標識. Body中的Parts 直接使用 @Part 標識。

@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);

同上文所說,Multipart parts也採用了Retrofit預設的序列化轉換器,如果你希望自己定義序列化,可以繼承RequestBody類,重寫序列化方法。

5. Retrofit設定請求頭的相關注解

你可以為一些方法設定請求頭欄位,這個需使用 @Headers 註解

@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();
@Headers({
    "Accept: application/vnd.github.v3.full+json",
    "User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);

請求頭欄位也可以被動態改變,在介面引數前加上 @Header就可以了。

額外說明:如果該引數傳入為null,那麼請求頭的該欄位會被刪除。該欄位的引數型別可以不是String型別,Retrofit最終會呼叫該引數的toString()方法,得到的值作為請求頭該欄位的值。

@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)

一路看下來,可能有讀者會問了,難道我需要為每個介面方法都設定一遍請求頭嗎?

不是的,對於所有介面都要用到的請求頭,可以通過 Okhttp攔截器統一設定。

三、Retrofit 的配置

通過上述描述,大家應該已經發現,Retrofit提供的功能就是把網路請求變成一個可呼叫的例項物件,這個物件裡面是Restful風格的各個網路請求介面。Retrofit提供了一些好的註解,幫我們減輕了封裝網路模組的工作。

Retrofit預設會為我們的平臺提供合適的,健壯的預設配置,但是這些配置我們也是可以自己定製的。

預設情況下,Retrofit 只能 把 HTTP Body型別 反序列化成 OKhttp的 ResponseBody型別,而且介面裡面預設接受的是 RequestBody型別,這個上文已經講過。

Retrofit支援配置其他的轉換器,來支援其他的型別,官網提供瞭如下6個可動態配置的libraries,需要哪個你就在Gradle裡面配置進來。

  • Gson: com.squareup.retrofit2:converter-gson
  • Jackson: com.squareup.retrofit2:converter-jackson
  • Moshi: com.squareup.retrofit2:converter-moshi
  • Protobuf: com.squareup.retrofit2:converter-protobuf
  • Wire: com.squareup.retrofit2:converter-wire
  • Simple XML: com.squareup.retrofit2:converter-simplexml
  • Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

下面是官網的例子,展示瞭如何使用其他的轉換器,這個只需要在建立Retrofit物件的時候動態傳入轉換器就可以了。下面例子使用了Gson作為反序列化工具。

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

GitHubService service = retrofit.create(GitHubService.class);

如果官網提供的轉換器無法滿足你,你可以自己繼承 Converter.Factory類,擴充套件你需要的功能

四、使用Retrofit

1. GRADLE配置

compile 'com.squareup.retrofit2:retrofit:2.1.0'

注意:Retrofit僅支援 Java 7 及 Android 2.3 以上版本。

2. PROGUARD配置

如果你的專案使用了混淆,請將如下程式碼新增到proguards-rules 資料夾當中。

# Platform calls Class.forName on types which do not exist on Android to determine platform.
-dontnote retrofit2.Platform
# Platform used when running on RoboVM on iOS. Will not be used at runtime.
-dontnote retrofit2.Platform$IOS$MainThreadExecutor
# Platform used when running on Java 8 VMs. Will not be used at runtime.
-dontwarn retrofit2.Platform$Java8
# Retain generic type information for use by reflection by converters and adapters.
-keepattributes Signature
# Retain declared checked exceptions for use by a Proxy instance.
-keepattributes Exceptions

相關文章