【一起學習Android開源框架】Retrofit註解解析(第三部分)

語言: CN / TW / HK

highlight: a11y-light theme: vue-pro


眾所周知,Retrofit底層是基於Okhttp實現的,與別網路框架不同,它更多的是使用執行時註解的方式提供相應的功能,在請求網路的時候更加便捷,下面我們就來簡單解析下Retrofit常用的一些註解


Retrofit的註解分類

相對於其他網路請求框架來說,最大的不同就是Retrofit使用了註解。 它的註解主要分為三大類

  • Http請求方法註解
  • 包含GET,POST,PUT,DELETE,HEAD,PATCH,OPTIONS 和HTTP
  • 標記類註解
  • 包含FromUrlEncoded,Multipart,Streaming
  • 引數類註解
  • 包含Header,Body,Path,Field,FieldMap,Part,PartMap,Query和QueryMap

關於Http請求方法註解,HTTP可以替換掉之前的七種方法,也可以使用擴充套件請求方法; 關於標記類註解,其中Streaming代表響應的資料以流的形式返回, 不使用它的時候預設會把全部資料載入到記憶體,所以下載大檔案的時候需要載入這個註解;

下面我會簡單介紹幾種常用的註解的用法,不當當只有GET和POST請求,實際上這些註解請求方法各自有各自的使用場景

GET請求訪問網路

首先我們用一個GET請求來請求網路,具體使用可以看官網例子或者前面的第一部分,這裡就不贅述了。使用Retrofit提供的@Get註解,顧名思義它代表的是GET請求;

一般GET請求,必須新增相對路徑或絕對路徑或者全路徑,如果不想在GET註解後新增請求路徑,則可以在方法的第一個引數中用@Url註解新增請求路徑,這個後面會說到

動態配置URL地址:@Path,@Url

​ 兩個註解都是動態配置URL地址,對於@Path在GET註解中包含了{path},它對應著@Path註解中的”path“,用來替換{path},也就是我們傳入的String值;對於@Url註解表示動態url請求資料,下面舉個例子

  • @Path ```kotlin /**
    • 知識體系下的文章
    • https://www.wanandroid.com/article/list/0/json?cid=168
    • @param page page
    • @param cid cid */ @GET("/article/list/{page}/json") fun getTreeArticleList( @Path("page") page: Int, @Query("cid") cid: Int ): Observable ```

對於這裡是去查詢知識體系下的文章,其中@Path註解會把路徑中的{page}替換成page引數實際的值,也就是分頁數,這樣就可以去實現分頁效果

  • @Url

kotlin var retrofit = Retrofit.Builder() .baseUrl("http://www.wanandroid.com/") .build() @GET fun getData(@Url user: String): Call<List<UserData>>

ps: 注意的是使用@Path,path對應的路徑不能包含”/”,不然每個加到host Url後面的東西都會被省略掉

動態指定查詢條件:@Query

​ 用於新增查詢引數,即請求引數,引數值是通過String.valueOf()轉換成String並且進行URL編碼,引數值通過String.valueOf()轉換為String並進行URL編碼

​ 使用該註解定義的引數,引數值可以為空,為空時,忽略該值,當傳入一個List或array時,為每個非空item拼接請求鍵值對,所有的鍵是統一的,比如說page = 1& page = 2 & page = 3

下面可以看下示例:

kotlin @GET("/list/json") fun getList(@Query("page") page: Int): Call<ResponseBody>

動態指定查詢條件組: @QueryMap

​ 用於以map的形式新增查詢引數,即請求引數,引數的鍵值對都是通過String.valueof()轉換為String格式,其中它的鍵和值都是預設進行URL編碼,map中每一項的鍵和值都不能為空,否則丟擲IllegalArgumentException異常

下面可以示例:

```kotlin

//不使用預設URL編碼
@GET("/search")
fun listOne(@QueryMap filters: Map<String?, String>): Call<ResponseBody>

//使用預設URL編碼
@GET("/search")
fun listTwo(@QueryMap(encoded = true) filters: Map<String?, String?>?): Call<ResponseBody>

```

POST請求網路

​ 簡單來說,@Post註解用於傳送一個post請求,一般必須新增相對路徑或絕對路徑或者全路徑,如果不想在POST註解後新增請求路徑,則可以在方法的第一個引數中用@Url註解新增請求路徑

傳輸資料型別為鍵值對:@Field

​ 當我們傳輸資料型別為鍵值對的時候,這也是我們最常用的POST請求資料型別,多用於以表單的形式上傳資料

kotlin @POST("/register") fun registerUser(@Field("id") userId: String): Call<ResponseBody>

@FieldMap和@Field的作用基本一樣的,不同的是它用於不確定引數個數的情況下

傳輸資料型別JSON字串:@Body

對於@Body,使用該註解定義的引數不可為nul,用POST方式將JSON字串作為請求體傳送到伺服器中,簡單來說,就是直接傳入一個實體類,retrofit會通過convert把該實體序列化並將序列化後的結果直接作為請求體傳送出去;

kotlin @POST("/login") fun login(@Body user: User): Call<ResponseBody>

單個檔案上傳:@Part

話不多說,先舉個例子

```kotlin
 @Multipart
    @POST("user/photo")
    fun updateUser(
        @Part photo: MultipartBody.Part,
        @Part("description") description: ResponseBody
    ): Call<ResponseBody>
```

MutiPart註解表示允許多個@Part,updateUser方法的第一個引數是準備上傳的圖片檔案,使用了MultipartBody.part型別;另一個引數是RequestBody型別,它用來傳遞簡單的鍵值對

多個檔案上傳:@PartMap

​ 多個檔案上傳和單檔案上傳是相似的,只是使用了Map封裝了上傳的檔案,並用@PartMap註解來標示起來,其他的都和單檔案上傳都一樣,這裡就不贅述了

kotlin @Multipart @POST("user/photo") fun updateUser( @PartMap photo: Map<String, ResponseBody>, @Part("description") description: ResponseBody ): Call<ResponseBody>

訊息報頭Header

在HTTP請求中,為了防止攻擊或者過濾掉不安全的訪問,或者需要新增加密等等,保證請求的安全,這時候通常都會在訊息報頭中攜帶一些特殊的訊息頭處理,所以Retrofit提供了@Header來新增訊息報頭,一般有兩種方式,靜態和動態

  • 靜態實現方式

kotlin @GET("some/endpoint") @Headers("Accept-Encoding: application/json") fun getType():Call<ResponseBody>

如果想要新增多個報頭,可以使用{}包含起來,@Headers註解程式碼已經是一個String的陣列,所以可以新增多個

kotlin @Headers( "Accept-Encoding: application/json", "User-Agent:MoonRetrofit") fun getCarType():Call<ResponseBody>

  • 動態實現方式

kotlin @GET("some/endpoint") fun getCarType2(@Header("Location") location: String) : Call<ResponseBody>

使用@Header註解,可以呼叫getCarType2介面來動態地新增訊息頭部,以上就是訊息頭部註解的簡單使用

  • 關於@Header和Headers

話不多說,直接上原始碼,可以直觀看到@Header和@Headers之間的區別(Headers是String陣列所以適用於多個請求頭的時候,而Header是動態新增的時候)

```java @Documented @Retention(RUNTIME) @Target(PARAMETER) public @interface Header { String value(); }

@Documented @Target(METHOD) @Retention(RUNTIME) public @interface Headers { String[] value(); } ```

額外說的話

  • 比較深入學習Retrofit後,會發現它的在一些易用性方面的問題,除了它是基於OKhTTP框架上封裝實現網路請求,除了基本的網路請求步驟,需要新增json解析器,GsonConvertFactory,來自動序列化json串,需要配置統一的cookie攔截器,這些程式碼需要你自己編寫,會比較麻煩
  • 對於Retrofit提供的這些註解,就是方面我們能更便捷的實現某些功能,但是這些東西還是需要自己去封裝,比如說上傳下載,有MulitPart和Streaming,但是我們是沒有辦法直接寫上傳下載的
  • 相信很多人使用Retrofit基本就是Get和post請求,我也是,對於各個方法的註解和引數的註解搭配還是比較懵逼的,而且我們還得遵守Retrofit的規則,否則就會出現各種各樣的問題

結語

  • 以上就是Retrofit中一些常用註解的使用解析,到這裡我們已經學會了如何使用Retrofit,包括它的簡單使用,常用的註解解析,以及它的使用代理模式,為接下來正式學習分析Retrofit原始碼做好鋪墊
  • 關於Retrofit更詳細的註解使用以及原理,上面還有很多註解沒有說明,大家可以去Retrofit官方網站以及網路其他資源學習
  • 參考資料:《Android進階之光》

未完待續