上一篇文章讲了RxJava,这一篇当然就该讲Retrofit了,参考资料:
https://blog.csdn.net/gumufuyun/article/details/83619879
1.Retrofit简介
Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装,网络请求的工作本质上是 OkHttp 完成,而 Retrofit 仅负责 网络请求接口的封装。在服务端返回数据之后,OkHttp 将原始的结果交给 Retrofit,Retrofit根据用户的需求对结果进行解析。
Retrofit 主要定义了 4 个接口:
- Callback<T>:请求数据的返回;
- Converter<F, T>:对返回数据进行解析,一般用 GSON ;
- Call<T>:发送请求,Retrofit 默认的实现是 OkHttpCall<T>,也可以依需自定义 Call<T>;
- CallAdapter<T>:将 Call 对象转换成其他对象,如转换成支持 RxJava 的 Observable对象
2.相关依赖
相关依赖:
//添加Retrofit依赖
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
//用Gson解析json的转换器
implementation 'com.squareup.retrofit2:converter-gson:2.0.2'
当然还有网络请求权限:
<uses-permission android:name="android.permission.INTERNET"/>
Retrofit将 Http请求 抽象成 Java接口:采用 注解 描述网络请求参数 和配置网络请求参数,用 动态代理 动态 将该接口的注解“翻译”成一个 Http的url请求,最后再执行 Http 请求。那我们先来解释Retrofit的各种注解吧。
3.注解类型
3.1网络请求方式的注解
网络请求中的网络请求方式的注解,如下:
-
网络请求方式之@GET、@POST、@PUT、@DELETE、@HEAD
相关代码如下:
//第一部分代码
/**
* 直接获得Responsebody中的内容,定义网络请求返回值为Call<ResponseBody>
* call<T> T是接受的数据返回类型
*/
@GET("article/list/1/json")
Call<ResponseBody> getCall();
//第二部分代码
Retrofit retrofit = new Retrofit.Builder()
//设置网络请求的Url地址
.baseUrl(baseUrl)
//设置数据解析器
.addConverterFactory(GsonConverterFactory.create()).build();
由上图代码可以看到Rtrofit的封装请求url至少由两部分组成:baseUrl+@Get()中的内容动态拼接而成,但不是绝对的,也可以:
retrofit = new Retrofit.Builder()
//设置网络请求的Url地址
.baseUrl("https://www.wanandroid.com/article/list/1/json")
//设置数据解析器
.addConverterFactory(GsonConverterFactory.create()).build();
实际开发中不会这样写的,而且第一部分和第二部分的代码也是分开写的,我这里是为了演示方便,理解。
上面的代码也可以通过@Http这样写:
- 网络请求方式之Http注解
/**
* method 请求方法
* path 请求路径,其中的{变量名}表示是一个变量
* hasBody 是否有请求体
* @param pageNum 参数
* @return
*/
@HTTP(method = "GET",path = "article/list/{pageNum}/json",hasBody = false)
Call<ResponseBody> getCallData(@Path("pageNum") int pageNum);
@Http注解的作用和@GET,@Post作用一样,可替换@GET、@POST、@PUT、@DELETE、@HEAD注解且进行更多功能拓展。
3.2 标记
-
@FormUrlEncoded
使用场景:表示发送form-encoded的数据,那么什么是FormUrlEncoded数据呢?
image.png
每个键值对需要用@Filed来注解键名,随后的对象需要提供值。
/**
* 表明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
* @param pageNum
* @return
*/
@GET("article/list/{pageNum}/json")
@FormUrlEncoded
Call<ResponseBody> getFormUrlEncodedData(@Field("pageNum") int pageNum );
-
@Multipart
使用场景:
作用:表示发送form-encoded的数据(适用于 有文件 上传的场景),也就是就是http请求中的multipart/form-data,它会将表单的数据处理为一条消息,以标签为单元,用分隔符分开。既可以上传键值对,也可以上传文件。当上传的字段是文件时,会有Content-Type来表名文件类型;content-disposition,用来说明字段的一些信息;由于有boundary隔离,所以multipart/form-data既可以上传文件,也可以上传键值对,它采用了键值对的方式,所以可以上传多个文件。如图:
image.png
每个键值对需要用@Part来注解键名,随后的对象需要提供值。
代码:
/**
{@link Part} 后面支持三种类型,{@link RequestBody}、{@link okhttp3.MultipartBody.Part} 、任意类型
* @param account 账号
* @param pwd 密码
* @param file file文件
* @return
*/
@GET("/form")
@Multipart
Call<ResponseBody> getMultipart(@Part("account") RequestBody account , @Part("pwd")
RequestBody pwd, @Part MultipartBody.Part file);
//具体执行代码:
//既可以提交普通键值对,也可以提交(多个)文件键值对。
public static final MediaType MEDIA_TYPE_MULTIPART_FORM = MediaType.parse("multipart/form-data;charset=utf-8");
RequestBody account =RequestBody.create(MEDIA_TYPE_MULTIPART_FORM,"123");
RequestBody pwd =RequestBody.create(MEDIA_TYPE_MULTIPART_FORM,"123");
RequestBody fileResponseBody =RequestBody.create(MEDIA_TYPE_MULTIPART_FORM,"134646464");
MultipartBody .Part file =MultipartBody.Part.createFormData("file","test.txt",fileResponseBody);
Call<ResponseBody> call = (Call<ResponseBody>) getMultipart(account, pwd, file);
//同步请求
Call<ResponseBody> result =call .excute();
3.3 网络请求参数
- @Header & @Headers
作用:添加请求头 &添加不固定的请求头
相关代码:
/ @Header
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
// @Headers
@Headers("Authorization: authorization")
@GET("user")
Call<User> getUser()
// 以上的效果是一致的。
// 区别在于使用场景和使用方式
// 1. 使用场景:@Header用于添加不固定的请求头,@Headers用于添加固定的请求头
// 2. 使用方式:@Header作用于方法的参数;@Headers作用于方法
- Body
作用:以 Post方式 传递 自定义数据类型 给服务器
特别注意:如果提交的是一个Map,那么作用相当于 @Field
不过Map要经过 FormBody.Builder 类处理成为符合 Okhttp 格式的表单,如:
FormBody.Builder builder = new FormBody.Builder();
builder.add("key","value");
- @Field & @FieldMap
作用:发送 Post请求 时提交请求的表单字段
具体使用:与 @FormUrlEncoded 注解配合使用
/**
* 表面明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
* @param pageNum
* @param pageSize
* @return
*/
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> getFormUrlEncodedData(@Field("pageNum") int pageNum ,@Field("pageSize") int pageSize);
/**
* 表面明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
* @param map
* @return
*/
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> getFormUrlEncodedMap(@FieldMap Map<String, Object> map);
//具体执行代码:
// @Field
Call<ResponseBody> call1 =HttpRequest.getInstance().getInterfaceInstance().getFormUrlEncodedData(1,15);
// @FieldMap
// 实现的效果与上面相同,但要传入Map
Map<String, Object> map = new HashMap<>();
map.put("pageNum", 1);
map.put("pageSize", 15);
Call<ResponseBody> call2 = HttpRequest.getInstance().getInterfaceInstance().getFormUrlEncodedMap(map);
- part & @PartMap
作用:发送 Post请求 时提交请求的表单字段
与@Field的区别:功能相同,但携带的参数类型更加丰富,包括数据流,所以适用于 有文件上传 的场景 - @Query和@QueryMap
作用:用于@GET
方法的查询参数(Query = Url 中 ‘?’ 后面的 key-value)
相关代码:
/**
* 登录接口
* @param username 用户名
* @param password 密码
* @return
*/
@POST("user/login")
Observable<BaseResponse<LoginOutBean>> login(@Query("username") String username , @Query("password") String password);
其使用方式同 @Field与@FieldMap.
-
@Path
作用:URL地址的缺省值 -
@Url
作用:直接传入一个请求的 URL变量 用于URL设置,当有URL注解时,@GET传入的URL就可以省略。4.实战操作
说了那么多不如来一个实战看看,这里以鸿洋大神的wanAndroid接口进行测试:
第一步定义请求接口类,用来管理所有的请求接口:

第二步:通过单例构建retrofit实例
public class HttpRequest {
private static volatile HttpRequest httpRequest;
private static String baseUrl = "https://www.wanandroid.com/";
private static Retrofit retrofit;
private static RequestInterface requestInterface;
private HttpRequest() {
//第二部分代码
retrofit = new Retrofit.Builder()
//设置网络请求的Url地址
.baseUrl(baseUrl)
//设置数据解析器
.addConverterFactory(GsonConverterFactory.create()).build();
//生成接口实例
requestInterface = retrofit.create(RequestInterface.class);
}
public static HttpRequest getInstance() {
if (httpRequest == null) {
synchronized (HttpRequest.class) {
if (httpRequest == null) {
httpRequest = new HttpRequest();
}
}
}
return httpRequest;
}
/**
* 返回接口实例的方法
*
* @return
*/
public RequestInterface getInterfaceInstance() {
return requestInterface;
}
}
第三步:执行相关请求
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//调用方法 返回Call对象
Call<ResponseBody> call = HttpRequest.getInstance().getInterfaceInstance().getCall(1);
//call对象执行异步请求,访问网络
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
String jsonString = null;
try {
jsonString =new String((response.body()).bytes());
//再使用Retrofit自带的Gson去解析
PageBean pageBean = new Gson().fromJson(jsonString, PageBean.class);
} catch (IOException e) {
e.printStackTrace();
}
Toast.makeText(MainActivity.this,jsonString,Toast.LENGTH_LONG).show();
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Toast.makeText(MainActivity.this,t.getMessage(),Toast.LENGTH_LONG).show();
}
});
}
});
简单就说这么多,如果你想更深的了解retrofit的源码,推荐文章:Retrofit想用得好就得这么死磕。
完毕!
网友评论