【一起学习Android开源框架】Retrofit注解解析(第三部分)

1,487 阅读7分钟

众所周知,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

        /**
         * 知识体系下的文章
         * 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<ArticleListResponse>
    

    对于这里是去查询知识体系下的文章,其中@Path注解会把路径中的{page}替换成page参数实际的值,也就是分页数,这样就可以去实现分页效果

  • @Url

        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

下面可以看下示例:

    @GET("/list/json")
    fun getList(@Query("page") page: Int): Call<ResponseBody>
动态指定查询条件组: @QueryMap

​ 用于以map的形式添加查询参数,即请求参数,参数的键值对都是通过String.valueof()转换为String格式,其中它的键和值都是默认进行URL编码,map中每一项的键和值都不能为空,否则抛出IllegalArgumentException异常

下面可以示例:


    //不使用默认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请求数据类型,多用于以表单的形式上传数据

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

@FieldMap和@Field的作用基本一样的,不同的是它用于不确定参数个数的情况下

传输数据类型JSON字符串:@Body

对于@Body,使用该注解定义的参数不可为nul,用POST方式将JSON字符串作为请求体发送到服务器中,简单来说,就是直接传入一个实体类,retrofit会通过convert把该实体序列化并将序列化后的结果直接作为请求体发送出去;

@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注解来标示起来,其他的都和单文件上传都一样,这里就不赘述了

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

消息报头Header

在HTTP请求中,为了防止攻击或者过滤掉不安全的访问,或者需要添加加密等等,保证请求的安全,这时候通常都会在消息报头中携带一些特殊的消息头处理,所以Retrofit提供了@Header来添加消息报头,一般有两种方式,静态和动态

  • 静态实现方式

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

    如果想要添加多个报头,可以使用{}包含起来,@Headers注解代码已经是一个String的数组,所以可以添加多个

    @Headers(
            "Accept-Encoding: application/json",
            "User-Agent:MoonRetrofit")
        fun getCarType():Call<ResponseBody>
    
  • 动态实现方式

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

    使用@Header注解,可以调用getCarType2接口来动态地添加消息头部,以上就是消息头部注解的简单使用

  • 关于@Header和Headers

    话不多说,直接上源码,可以直观看到@Header和@Headers之间的区别(Headers是String数组所以适用于多个请求头的时候,而Header是动态添加的时候)

    @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进阶之光》

未完待续