Retrofit在android中已经流行很长时间了,很多人用的很熟练,但是就Retrofit的注解来说,还有人只是记下来这么做了,但是什么场景下使用什么注解,为什么要使用这样的注解还不太懂,跟我第一次使用是一样的,所以这里分享一下自己的心得。
说注解前,先说下HTTP的请求。App发送一个请求,是需要App端把一个请求组装成请求报文,这个报文由三部分组成:请求行、请求头、请求体。使用Retrofit来完成api的请求,Retrofit会为我们完成报文的组装。
看一个完整的请求报文格式:
1)请求体:POST和PUT请求有请求体,其他方法没有请求体。
2)请求行:请求行由三部分组成,分别是method、path、http version。
3)请求头:请求头的内容很多,我们只拿host和content-type这2个重要参数来说。
1.Get请求常用注解
看一下使用例子:
@GET("sample/{id}/user")
Call<Object> sample(@Path("id") long id, @Query("type") String type);
首先是添加了一个@GET注解,表明这是一个get请求,括号中的内容是请求的路径,那么Retrofit会为我们组装请求行,此时如果不需要额外的请求头信息,例如添加一个token,那么请求头是不需要做任何事情的。那么Retrofit给我们做成的请求报文为:
GET http://api.xxx.com/sample/1312/user?type=1 http/1.1
Host:api.xxx.com
Content-Type:text/plain
在get的path中:"sample/{id}/user",id是path中的一个占位参数,实际赋值使用的是一个@Path,因为id就是在path中,所以使用@Path注解;type参数是get中key-value键值对,是根据该值来查询的,所以使用@Query注解。
2.POST请求常用注解
post的使用场景比较多,如何使用注解由请求报文中请求头中的Content-Type的值来决定,Content-Type的值虽然是由我们客户端来设置的,但是客户端根据什么来设置呢?这个需要看后台接收参数需要什么类型的,后台需要什么类型,我们就设置什么类型的值。
这里我们设定一个场景,注册一个用户,使用账号和密码的方式来注册。
1.如果后台规定使用普通表单的方式来注册,那么我们的Content-Type值需要定义为:application/x-www-urlencoded。对应的Retrofit的使用为:
@FormUrlEncoded
@POST("classify")
Call<Object> postSelect(@Field("param1") String param1, @Field("param2") String param2);
首先,@FormUrlEncoded的意思就是:Retrofit在组装请求头的Content-Type时,把其值赋值为application/x-www-urlencoded。如果接口需要接收额外的参数,比如param1和param2,那么使用注解@Field来完成。此时因为是POST请求,所以会有请求体,Retrofit会根据param1和param2来组装请求体,并且是以键值对的方式来组装,如果有多个参数,则使用&符号做拼接。组装后的请求报文为:
POST /classify HTTP/1.1
Host:api.xxx.com
Content-Type:application/x-www-urlencoded
param1=aaa¶m2=bbb
2.如果后台需要接收的参数是一个Json字符串,那么Content-Type值需要定义为:application/json。对应的Retrofit的使用为:
@POST("classify")
Call<Object> postSelect(@Body User user);
使用Json比较简单,POST上面不需要添加额外的参数注解来声明Content-Type的值,使用@Body来上传一个json字符串,这里的User就是一个普通的java Bean对象,使用时会自动转成json,并且会给Content-Type赋值为application/json。组装后的请求报文为:
POST /classify HTTP/1.1
Host:api.xxx.com
Content-Type:application/json
{"account":"erge","password":"111111"}
3.如果注册比较特殊,比如注册时必须要使用头像,则后台接收的参数就不止2个普通的字符串参数,还需要添加图片文件,注意这里图片文件不适用Base64的方式,如果使用Base64的方式,则图片也就是一个字符串,我们把上面一种方法直接添加一个参数就可以了。这个场景就是直接使用图片文件进行上传,那么此时后台就需要把Content-Type定义为multipart/form-data,从字面意思来看Content-Type的值就是:表单,并且是多部分的,简单理解就是表单中有多重类型的参数,其实就是字符串和二进制文件,字符串就是普通的参数,二进制就是文件。那么Retrofit该如何使用呢:
@MultiPart
@POST("classify/select")
Call<Object> postSelect(@Part("account") RequestBody account,
@Part("password") RequestBody password,
@Part("avatar") RequestBody avatar);
RequestBody accountBody = RequestBody.create(MediaType.parse("txt/plain"), account);
RequestBody passwordBody = RequestBody.create(MediaType.parse("txt/plain"), password);
RequestBody avatarBody = RequestBody.create(MediaType.parse("image/jpeg"), imageFile);
Services.service.postSelect(accountBody,passwordBody,avatarBody);
首先我们需要使用@MultiPart注解,Retrofit遇到该注解会将Content-Type定义为multipart/form-data,那body信息是如何来组装的呢?此时body既不是上面的键值对方式也不是json形式,因为有二进制文件,所以不可能是这两种方式,他组装是通过一个boundary来分区参数的,没2个boundary之间是一个参数,有兴趣的可以给Retrofit添加日志拦截,查看下具体的log。
使用Retrofit的一般场景就是这些,了解了Http的Content-Type是什么意思,就知道了如何使用Retrofit的注解,再也不用背什么场景下使用哪种注解了,跟后台调试接口时也不用被动的去试后台接收图片或者接收多个参数是如何规定的了