我还记得2017年的时候就已经有同事慢慢开始使用Retrofit网络框架了,在2018年的时候也是同事封装好我直接使用的,索性2020年的时候自己写新项目,借着这个机会巩固封装了一波,只是时隔几个月才来记录,起初久久无法落笔,因为想写的有点多,最终决定分为几个部分进行记录 ~
兄弟篇
嗯哼,你为什么使用Retrofit啊?
嗯…嗯… 因为Retrofit是基于okHttp进行的二次封装,那么就首先就具备了okHttp的优势,即
okHttp优点(远不止以下几点)
okHttp缺点(远不止以下几点)
At the same time
,Retrofit支持了RxJava、RxAndroid的响应式编程,可快速动态切换子主线程,从而实现同步、异步的请求,这样在弥补了okHttp的不足之余,还二次进行了升级;
Last
,Retrofit是一款注解型的网络框架,使用简单,扩展性强、耦合性低,安全性高、同时支持动态解析Model,可配置不同HTTP client来实现网络请求,如okhttp、httpclient等;
Retrofit的注解分类,我个人主要将其分为四种类型,即请求类、标记类、参数类
类型 | 注解 | 作用范围 |
---|---|---|
请求类 | @GET | |
@POST | ||
@PUT | ||
@DELETE | ||
@PATH | ||
@HEAD | ||
@OPTIONS | ||
@HTTP | ||
标记类 | @FormUrlEncoded | Post请求 |
@Multipart | Post请求、图片、文件上传 | |
@Streaming | Get请求、 图片、文件下载 | |
参数类 | @Path | 通用 |
@Header | 通用 | |
@Headers | 通用 | |
@Url | 通用 | |
@Query | Get请求 | |
@QueryMap | Get请求 | |
@Body | Post请求 | |
@Field | Post请求 | |
@FieldMap | Post请求 | |
@Part | 图片、文件上传 | |
@PartMap | 图片、文件上传 |
日常开发中我们常会将网络请求的rul抽取在一个类中,BaseUrl 一般为服务器地址,其余url一般为对应接口的请求地址 ~
//服务器地址,每个人都不同
BaseUrl="http://test.retrofit.cn/"
//接口地址
ProductUrl="getProduct"
在网络请求中我们需要根据接口的返回体创建对应的model用于json接收和解析,所以T泛指当前接口返回的对应实体~
常规的话我们会和后台约定一个外层的基础model,然后内置不同的model,这点可查看我后续的文章 ~
示例:Get无参请求,这里的T指的更像是ProductBean这样的model
@GET("getProduct")
Call<T>getProductList();
这里并非要开始讲Http协议、TLS、SSL、四层结构、七层结构、安全证书等等(哈哈....)
主要说一下在Http请求中,常见请求方式的使用场景(虽然下面也有写,但是先简单说一下)
Put 请求
:常用于新增、更新数据的场景Get请求
:常用于获取数据的场景Post请求
:常用于安全获取数据的场景Delete请求
:常用于删除数据的场景请求方式:@PUT、@GET、@POST、@DELETE、@PATCH、@OPTIONS、@HEAD、@HTTP
常见的请求方式应该就是Get、Post
请求了,但是Retrofit
还支持了其他的多种请求方式用于适应更多场景 ~
HTTP版本 | 请求方式 |
---|---|
1.0 | GET |
POST | |
HEAD | |
1.1 | PUT |
DELETE | |
OPTIONS | |
TRACE | |
CONNECT |
使用对应请求方式时,需要将对应注解声明在方法上方,目前主要涉及8种请求方式,除Get、Post之外的请求方式具体如下 ~
@PUT
:提交资源或者更新资源(将资源提交到服务器,如果请求的url地址已经在服务器上存在对应的资源了,则put请求提交的实体则会对其进行修改)
@PATCH
:PATCH请求是对PUT请求的补充,用于局部更新资源
@DELETE
:DELETE方法请求源服务器删除Request-URI标识的资源(如果响应包括描述状态的实体,则成功响应应为200(OK),如果操作尚未执行,则应为202(已接受);如果操作已颁布但响应不包括,则应为204(无内容)一个实体)
@HEAD
:HEAD方法与GET相同,只是服务器不能在响应中返回消息体
响应HEAD请求的HTTP头中包含的元信息应该与响应GET请求时发送的信息相同;该方法可用于获得关于请求所暗示的实体的元信息,而无需转移实体主体本身。此方法通常用于测试超文本链接的有效性,可访问性和最近的修改
对HEAD请求的响应是可缓存的,因为响应中包含的信息可用于从该资源更新先前缓存的实体;如果新字段值指示缓存的实体与当前实体不同(如Content-Length,Content-MD5,ETag或Last-Modified中的更改所示),则缓存必须将缓存条目视为陈旧
使用场景
@OPTIONS
:主要用途有俩种@HTTP
该注解其实是通用注解,可动态配置其余7种请求方式,相比其他注解请求专一性弱,扩展性高,所以在开发中很少使用~
Http参数
以Get、Post为例
@HTTP(method = "GET", path = "getProduct", hasBody = false)
Call<T> getProductList();
@FormUrlEncoded
@HTTP(method = "POST", path = "getProduct", hasBody = true)
Call<T> getProductList(@FieldMap Map<String, String> params);
接口地址,出现在声明接口的场景中
,主要有俩种设置方式
入门写法
:方法参数内通过@Url
进行添加 @GET
Call<T> getProductList(@Url String url);
常规写法
:请求方式后进行添加 @GET("getProduct")
Call<T>getProductList();
地址参数,出现在需要动态补全请求地址的场景中
单参
示例1
@GET("getProduct/{userId}")
Call<T> getProductList(@Path("userId")String userId);
示例 2
@GET("getProduct/{userId}/date")
Call<T> getProductList(@Path("userId")String userId);
多参
@GET("getProduct/{userId}/{time}")
Call<T> getProductList(@Path("userId") String userId, @Path("time")String time);
在开发实战中,每逢和后台沟通接口时总会有请求头浮现,所有该注解还是蛮常用的 ~
在开发使用中,一般关于请求头的设置方式有三种
请求方法内
通过入参方式使用@Header注解
,相对动态请求方法上
通过@Header 、@Headers
传入对应参数,相对静态Retrofit自定义过滤器传入所有接口都需要传入的固定请求头(常用)
( 封装一个属于自己的 RxJava、RxAndroid + Retrofit 网络框架)here:具有相同名称的请求头不会相互覆盖,而是会照样添加到请求头中
设置方式
@Heade 单请求头(动态)
@FormUrlEncoded
@POST("getProduct")
Call<T> getProductList(@Header("token") int token);
@Headers多请求头(静态)
@Headers({
"header1:headerValue1",
"header2:headerValue2",
})
@POST("getProduct")
Call<T> getProductList();
最常见的请求方式之一,相比post优势在于明文请求,测试相对简单,缺点就是安全性低一些 ~
主要使用注解: @GET、@Query、@QueryMap
无参请求
(实际路径:http://test.retrofit.cn/getProduct) @GET("getProduct")
Call<T> getProductList();
单参请求
(实际路径:http://test.retrofit.cn/getProduct?userId={userId参数}) @GET("getProduct")
Call<T> getProductList(@Query("userId") String userId);
多参请求
(实际路径:http://test.retrofit.cn/getProduct?userId={userId参数}&time={time参数})此方式其实还是由单参组合而成,请继续往下看
@GET("getProduct")
Call<T> getProductList(@Query("userId") String userId, @Query("time")String time);
一般多参Get请求,我们使用@QueryMap注解,传入对应类型的map,具体参数继续往下看
@GET("getProduct")
Call<T> getProductList(@QueryMap Map<String, String> map);
map传入方式,内部包含了后台要求的字段信息
HashMap<String, Object> map = new HashMap<>();
map.put(userId,"传入userId");
map.put(time,"传入time");
相比Get请求而言,Post请求主要在于提升了数据的安全性
,在实现方面除注解名不同外,实现方法相似;但要注意@FormUrlEncoded注解
~
主要使用注解: @POST、@FormUrlEncoded、@Field、@FieldMap
ps:关于使用@FormUrlEncoded注解时要注意,其使用场景一般都建立在Post请求需要传入参数的场景下,如无需传入参数则不需要调用@FormUrlEncoded注解!
- 使用@FormUrlEncoded注解,表示请求正文将使用表单网址编码
- 自带”application / x-www-form-urlencoded” MIME类型,字段名称和值将先进行UTF-8进行编码,再根据RFC-3986进行URI编码
无参请求
@POST("getProduct")
Call<T> getProductList();
单参请求
@FormUrlEncoded
@POST("getProduct")
Call<T> getProductList(@Field("userId") String userId);
多参请求
(实际路径:http://test.retrofit.cn/getProduct?userId={userId参数}&time={time参数})此方式其实还是由单参组合而成,请继续往下看
@FormUrlEncoded
@POST("getProduct")
Call<T> getProductList(@Field("userId") String userId, @Field("time")String time);
一般多参Post请求,我们使用@QueryMap注解,传入对应类型的map,具体参数继续往下看
@FormUrlEncoded
@POST("getProduct")
Call<T> getProductList(@FieldMap Map<String, String> map);
map传入方式,内部包含了后台要求的字段信息
HashMap<String, Object> map = new HashMap<>();
map.put(userId,"传入userId");
map.put(time,"传入time");
该注解常用于Post请求,一般建立在传参较多且较乱的复杂场景
下,关于它的使用大家需要注意几点
需创建传参对应的实体包装类
,如下方的TBean(关于实体类最好是public权限,同时声明有参无参、get/set方法)@Body标签不能和@FormUrlEncoded或@Multipart标签同时使用
(会报错) //接口
@POST("getProduct")
Call<ResponseBody> getProductList(@Body TBean bean);
//实体类
public class TBean {
private String userId;
private String time;
public TBean(String userId, String time) {
this.userId = userId;
this.time = time;
}
public TBean() {
super();
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
}
这个请求方式比较个性一点,首先这种请求方式并不常用,同时删除方式有点区别(这里是在我2022年的时候补全的,用的是协程做的请求,大家看自己需要关注的就行)
path传参
@DELETE("member/dynamic/{dynamicNo}")
suspend fun delMemberDynamic(@Path("dynamicNo") dynamicNo: String): TNResponse<Boolean>
body传参
(注意:使用 @HTTP注解的原始方法进行实现
) @HTTP(method = "DELETE", path = "member", hasBody = true)
suspend fun delMember(@Body edit: UpdatePhone): TNResponse<Boolean>
@Part和@PartMap注解使用场景相对比较有针对性,一般都是用于图片上传、文件上传的场景 !
主要使用注解: @Multipart、@Part、@PartMap
上传方式
//单张上传
@Multipart
@POST("upload")
Call<T> uploadFile(@Part() RequestBody file);
//图文上传
@Multipart
@POST("upload")
Call<T> upload(@Part("description") RequestBody description, @Part MultipartBody.Part file);
//多张上传
@Multipart
@POST("upload")
Call<T> uploadFiles(@PartMap Map<String, RequestBody> param);
辅助工具
因传入参数类型基本为RequestBody类型,故可使用下方俩个方法快速转换
public static RequestBody toRequestBodyOfText (String value) {
RequestBody body = RequestBody.create(MediaType.parse("text/plain"), value);
return body ;
}
public static RequestBody toRequestBodyOfImage(File pFile){
RequestBody fileBody = RequestBody.create(MediaType.parse("image/*"), pFile);
return fileBody;
}
调用场景 - 图文上传
//需要上传的文件
File file = new File(path);
//不论是文本或图片文件均可通过辅助工具修改其MediaType
RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
//使用MultipartBody.Part时需要和服务端约定好Key,这里的part name是我们自定的image
MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestBody);
// 添加上传文件描述
String descriptionString = "文件描述";
RequestBody description = RequestBody.create(MediaType.parse("multipart/form-data"), descriptionString);
扩展
@Part扩展
okhttp3.MultipartBody.Part
,内容将被直接使用;RequestBody
,那么该值将直接与其内容类型一起使用;@PartMap扩展
Retrofit中下载的主要注解方式,一般常见于图片下载,具体使用如下 ~
@GET
@Streaming
Call<ResponseBody> downloadImage(@Url String url);
参考Blog