上一篇文章讲了RxJava,这一篇当然就该讲Retrofit了,参考资料: https://blog.csdn.net/gumufuyun/article/details/83619879
Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装,网络请求的工作本质上是 OkHttp 完成,而 Retrofit 仅负责 网络请求接口的封装。在服务端返回数据之后,OkHttp 将原始的结果交给 Retrofit,Retrofit根据用户的需求对结果进行解析。
Retrofit 主要定义了 4 个接口:
相关依赖:
//添加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的各种注解吧。
网络请求中的网络请求方式的注解,如下:
//第一部分代码
/**
* 直接获得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这样写:
/**
* 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注解且进行更多功能拓展。
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 );
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();
/ @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作用于方法
FormBody.Builder builder = new FormBody.Builder();
builder.add("key","value");
/**
* 表面明是一个表单格式的请求(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);
@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.
说了那么多不如来一个实战看看,这里以鸿洋大神的wanAndroid接口进行测试: 第一步定义请求接口类,用来管理所有的请求接口:
image.png
第二步:通过单例构建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想用得好就得这么死磕。 完毕!