目录
Retrofit是Square公司出品的网络请求封装库,注意是封装框架,不是网络请求框架;Retrofit底层网络请求是通过OKHttp实现的,Retrofit只是在此基础上做了很好的封装,使其更好用,更方便,更强大,而且能很多开源库(比如:GSON,Rxjava)配合使用;把网络请求通过注解封装成一个接口;
官网地址
https://github.com/square/retrofit
https://square.github.io/retrofit/#introduction
网络请求是获取百度搜索页的数据
1,引入Retrofit2依赖,由于Retorift2已经内置了OKHttp所以不需要额外引入OkHttp了;
implementation 'com.squareup.retrofit2:retrofit:2.6.2'//Retorift2依赖
implementation 'com.squareup.retrofit2:converter-gson:2.6.2'//数据解析转换器
implementation 'com.squareup.retrofit2:adapter-rxjava:2.6.2'//支持RxJava
注意:后面两个依赖可以不加, 如果不加拿到网络请求原始数据(例如接口返回的原始数据)之后自己做解析转换,或者和RxJava对接;也可以加上,这样就不用自己解析数据,可以通过添加的数据转换器转换自己所需的数据类型(例如 Observable<Java Bean>)或Java Bean;
2,添加网络请求权限
<uses-permission android:name="android.permission.INTERNET"/>
3,创建Retrofit实例,baseUrl方法参数不可以为空;必须以http或者https开头; 必须以 /(斜线) 结束,代表根路径;但是有隐含的 / 就不需要了;
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.baidu.com")
.addConverterFactory(new Converter.Factory() {
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
//return super.responseBodyConverter(type, annotations, retrofit);
return new Converter<ResponseBody, String>() {
@Override
public String convert(ResponseBody value) throws IOException {
//return null;
return value.string();
}
};
}
}
)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
4,创建网络请求Java接,一个接口可以定义多个网络请求方法;Retrofit采用注解描述网络请求参数和配置网络请求参数,有关Retrofit的注解参数的使用下面单独接受;
public interface HttpService {
@GET("/") //由于我们是解析首页,也就是根目录,所以这边写"/"
Call<String> baidu();
}
5,用Retrofit对象,通过动态代理创建网络请求接口的代理类;
HttpService service = retrofit.create(HttpService.class);
6,根据网络请求接口的代理对象,创建实际发起网络请求的Call对象
Call<String> call = service.baidu();
7,发起网络请求
//异步请求
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
//处理网络请求返回的数据
System.out.println("测试Retrofit");
System.out.println(response.body());
}
@Override
public void onFailure(Call<String> call, Throwable t) {
}
});
//同步请求
Response<String> response = call.execute();
//处理网络请求返回的数据
System.out.println(response.body()); //esponse.body()就是String对象
注意:网络请求的完整Url = 在创建Retrofit实例时通过.baseUrl()设置的 + 网络请求接口的注解设置(下面称 “path“ )
path = "/a/b"是绝对路径
path="a/b"是先对路径;
path是绝对路径时,会覆盖baseUrl除Ip和端口以外的路径;
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://192.168.1.1:8080/aa/").build();
@POST("/b/c")
Call<ResponseBody> login(@Body RequestBody requestBody);完整地址是 http://192.168.1.1:8080/b/c 不是原本的 http://192.168.1.1:8080/aa/b/c
总共22个注解,根据功能大概分为三类:
请求方法类型
@GET、@POST、@PUT、@DELETE、@PATCH、@HEAD、@OPTIONS、@HTTP
标记类型
@FormUrlEncoded、@Multipart、@Streaming
参数类型
@Headers、@Header、@Body、@Field、@FieldMap、@Part、@PartMap、@Query、@QueryMap、@Path、@URL
1,网络请求方法
@GET、@POST、@PUT、@DELETE、@PATCH、@HEAD、@OPTIONS、@HTTP和Http网络请求方法(GET,POST,PUT...等请求方法)一一对应;
Http请求方法 | Retrofit注解名称 | 关系 |
GET | @GET | 所有注解分别对应Http网络请求中的方法,都接收网络地址Url |
POST | @POST | |
PUT | @PUT | |
DELETE | @DELETE | |
PATCH | @PATCH | |
HEAD | @HEAD | |
OPTIONS | @OPTIONS | |
@HTTP | 用来替换以上其中注解,可以扩展其功能 |
例如:通过GET方法获取必应每日图片的数据
URL完整路径:https://cn.bing.com/HPImageArchive.aspx?format=js&idx=1&n=1&nc=1498657985239&pid=hp&video=1
public static void main(String[] args) {
Map<String, String> params = new HashMap<>();
params.put("format", "js");
params.put("idx", "1");
params.put("n", "1");
params.put("pid", "1498657985239");
params.put("nc", "hp");
params.put("video", "1");
getBingDate(params);
}
public interface BingImageUtil {
//通过HTTP注解替换GET
/**
* method:网络请求的方法(区分大小写)
* path:网络请求地址路径
* hasBody:是否有请求体
*/
// @HTTP(method = "GET", path = "HPImageArchive.aspx?", hasBody = false)
// Call<ResponseBody> getBingImage(@QueryMap Map<String, String> params);
@GET("HPImageArchive.aspx?")
Call<ResponseBody> getBingImage(@QueryMap Map<String, String> params);
}
//URL全路径:https://cn.bing.com/HPImageArchive.aspx?format=js&idx=1&n=1&nc=1498657985239&pid=hp&video=1
public static void getBingDate(Map<String,String> params) {
Retrofit retrofit = new Retrofit.Builder().baseUrl("https://cn.bing.com/").build();
BingImageUtil bingImageUtil = retrofit.create(BingImageUtil.class);
Call<ResponseBody> call = bingImageUtil.getBingImage(params);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
System.out.print("必应每日图片获取数据 " + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
}
2,标记类型
Retrofit注解名称 | 释义 | 使用位置 |
@FormUrlEncoded | 表示请求体是一个From表单 | 网络请求接口的方法上 |
@Multipart | 表示请求体是一个支持文件上传的Form表单 | |
@Streaming | 表示请求的数据以流的形式返回;未使用该注解,默认会把数据全部载入内存,之后通过流获取数据也是读取内存中数据,所以返回数据较大时,需要使用该注解,不然会产生OOM |
2.1,@FormUrlEncoded
每个键值对需要用请求参数@Filed来注解键名,随后的对象是需要提供的值。
@FormUrlEncoded
@POST("/form")
Call <ResponseBody> workPlanList(@Field("type") String type);
2.2,Multipart
每个键值对需要用请求参数@Part来注解键名,随后的对象是需要提供值。
@Multipart
@POST("game/findgamebinsb")
Call<ResponseBody> gameList(@PartMap Map<String, RequestBody> requestBodyMap);
@FormUrlEncoded注解和@Multipart注解案例
public interface HttpService_Interface {
/**
*表明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
* @Field("username")表示将后面的 String name 中name的取值作为 username 的值
*/
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> testFormUrlEncoded1(@Field("username") String name, @Field("age") int age);
/**
* @Part后面支持三种类型,RequestBody、 okhttp3.MultipartBody.Part 、任意类型
* 除okhttp3.MultipartBody.Part以外,其它类型都必须带上表单字段(okhttp3.MultipartBody.Part中已经包含了表单字段的信息),
*/
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpload1(@Part("name") RequestBody name,@Part("age") RequestBody age, @Part MultipartBody.Part file);
//使用
private void test() {
HttpService_Interface service = retrofit.create(HttpService_Interface.class);
// @FormUrlEncoded
Call<ResponseBody> call1 = service.testFormUrlEncoded1("Carson", 24);
// @Multipart
// RequestBody name = RequestBody.create(textType, "lza");
RequestBody name = RequestBody.create(MediaType.parse("application/json;charset=UTF-8"), "lza");
RequestBody age = RequestBody.create(MediaType.parse("application/json;charset=UTF-8"), "26");
File file = new File("fileUrl"); //上传一个文件
// 创建 RequestBody,用于封装构建RequestBody
// RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
RequestBody requestFile = RequestBody.create(MediaType.parse("image/jpg"), file);
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", "test.txt",requestFile);
Call<ResponseBody> call2 = service.testFileUpload1(name,age, filePart);
}
2.3,Streaming
@Streaming
@GET("/update_apk")
Call<ResponseBody> downloadFile(@Url String fileUrl);
@Headers、@Header、@Body、@Field、@FieldMap、@Part、@PartMap、@Query、@QueryMap、@Path、@URL
注解名称 | 作用 | 使用位置 |
@Headers | 用于添加请求头 | 请求接口的方法上 |
@Header | 添加不固定值的Header | 请求接口的方法参数中 |
@Field | 向Post表单中传键值对,与@FormUrlEncoded注解配合使用 | |
@FieldMap | ||
@Part | 表单字段,与@Multipart注解配合使用,适合有文件上传的情况 | |
@PartMap | ||
@Query | 用于表单字段,作用和@Field@FieldMap相同;区别:@Query和@QueryMap中的数据体现在Url上,而@Field和@FieldMap的数据是请求体,但最后生成的数据形式是一样的 | |
@QueryMap | ||
@Body | 非表单请求体 | |
@Path | URL缺省值 | |
@URL | URL设置 |
详细说明
@Headers、@Header
区别在于使用场景和使用方式 :1, 使用场景:@Header用于添加不固定的请求头,@Headers用于添加固定的请求头 2, 使用方式:@Header作用于方法的参数;@Headers作用于方法
// @Header
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
// @Headers
@Headers("Authorization: authorization")
@GET("user")
Call<User> getUser()
// 以上的效果是一致的。
@Body
@POST("push/receive")
Call<ResponseBody> pushCallBack(@Body RequestBody route);
@Field、@FieldMap
与@FormUrlEncoded注解配合使用
/**
*表明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
* @Field("username")表示将后面的 String name 中name的取值作为 username 的值
*/
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> testFormUrlEncoded1(@Field("username") String name, @Field("age") int age);
@Part、@PartMap
与@Multipart注解配合使用
/**
* @Part后面支持三种类型,RequestBody、 okhttp3.MultipartBody.Part 、任意类型
* 除okhttp3.MultipartBody.Part以外,其它类型都必须带上表单字段(okhttp3.MultipartBody.Part中已经包含了表单字段的信息),
*/
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpload1(@Part("name") RequestBody name,@Part("age") RequestBody age, @Part MultipartBody.Part file);
@Query、@QueryMap
用于 @GET
方法的查询参数
@GET("game/getOneGame")
Call<ResponseBody> gameDetail(@Query("gameid") String gameid);
@Path
URL地址的缺省值
注1:{占位符}和path尽量只用在URL的path部分,url中的参数使用@Query和@QueryMap 代替,保证接口定义的简洁
注2:@Query、@Field和@Part这三者都支持数组和实现了Iterable接口的类型,如List,Set等,方便向后台传递数组。
@GET("users/{user}/repos")
Call<ResponseBody> getBlog(@Path("user") String user );
// 访问的API是:https://api.github.com/users/{user}/repos
// 在发起请求时, {user} 会被替换为方法的第一个参数 user(被@Path注解作用)
@URL
直接传入一个请求的 URL变量 用于URL设置
@GET
Call<ResponseBody> testUrlAndQuery(@Url String url, @Query("isShow") boolean isShow);
// 当有URL注解时,@GET传入的URL就可以省略
// 当GET、POST...HTTP等方法中没有设置Url时,则必须使用 {@link Url}提供