一、概述
1、什么是Retrofit
Retrofit
是现在比较流行的网络请求框架,可以理解为okhttp
的加强版,底层封装了okhttp
。准确来说,Retrofit
是一个RESTful
的http
网络请求框架的封装。因为网络请求工作本质上是由okhttp
来完成,而Retrofit
负责网络请求接口的封装。
本质过程:App应用程序通过Retrofit
请求网络,实质上是使用Retrofit
接口层封装请求参数、Header
、Url
等信息,之后由okhttp
来完成后续的请求工作。在服务端返回数据后,okhttp
将原始数据交给Retrofit
,Retrofit
根据用户需求解析。(源码在文章最后给出)
2、Retrofit的优点
- 超级解耦 ,接口定义、接口参数、接口回调不在耦合在一起
- 可以配置不同的
httpClient
来实现网络请求,如okhttp
、httpclient
- 支持同步、异步、
Rxjava
- 可以配置不同反序列化工具类来解析不同的数据,如json、xml
- 请求速度快,使用方便灵活简洁
二、注解
Retrofit
使用大量注解来简化请求,Retrofit
将okhttp
请求抽象成java接口,使用注解来配置和描述网络请求参数。大概可以分为以下几类,我们先来看看各个注解的含义,再一一去实践解释。
1、请求方法注解
请求方法注解 | 说明 |
---|---|
@GET | get请求 |
@POST | post请求 |
@PUT | put请求 |
@DELETE | delete请求 |
@PATCH | patch请求,该请求是对put请求的补充,用于更新局部资源 |
@HEAD | head请求 |
@OPTIONS | options请求 |
@HTTP | 通过注解,可以替换以上所有的注解,它拥有三个属性:method、path、hasBody |
2、请求头注解
请求头注解 | 说明 |
---|---|
@Headers | 用于添加固定请求头,可以同时添加多个,通过该注解的请求头不会相互覆盖,而是共同存在 |
@Header | 作为方法的参数传入,用于添加不固定的header,它会更新已有请求头 |
3、请求参数注解
请求参数注解 | 说明 |
---|---|
@Body | 多用于Post请求发送非表达数据,根据转换方式将实例对象转化为对应字符串传递参数,比如 使用Post发送Json数据,添加GsonConverterFactory则是将body转化为json字符串进行传递 |
@Filed | 多用于Post方式传递参数,需要结合@FromUrlEncoded使用,即以表单的形式传递参数 |
@FiledMap | 多用于Post请求中的表单字段,需要结合@FromUrlEncoded使用 |
@Part | 用于表单字段,Part和PartMap与@multipart注解结合使用,适合文件上传的情况 |
@PartMap | 用于表单字段,默认接受类型是Map<String,RequestBody>,可用于实现多文件上传 |
@Path | 用于Url中的占位符 |
@Query | 用于Get请求中的参数 |
@QueryMap | 与Query类似,用于不确定表单参数 |
@Url | 指定请求路径 |
4、请求和响应格式(标记)注解
标记类注解 | 说明 |
---|---|
@FromUrlCoded | 表示请求发送编码表单数据,每个键值对需要使用@Filed注解 |
@Multipart | 表示请求发送form_encoded数据(使用于有文件上传的场景),每个键值对需要用@Part来注解键名,随后的对象需要提供值 |
@Streaming | 表示响应用字节流的形式返回,如果没有使用注解,默认会把数据全部载入到内存中,该注解在下载大文件时特别有用 |
三、Retrofit的使用
1、注解详解
我们先来解释注解的使用和需要注意的问题(具体的使用步骤后面会给出)。上面提到注解是用来配置和描述网络请求参数的,我们来逐个讲解一下,首先创建网络接口类:
Retrofit
将okhttp
请求抽象成java接口,采用注解描述和配置网络请求参数,用动态代理将该接口的注解“翻译”成一个Http
请求,最后执行Http
请求。- 注意:接口中的每个方法的参数都要用注解标记,否则会报错。
(1)@GET、@Query、@QueryMap的使用
public interface Api {
// get请求 @GET("user") Call getData(); }
@GET
请求方法注解,get
请求,括号内的是请求地址,Url
的一部分Call<*>
返回类型,*
表示接收数据的类,一般自定义getData()
接口方法名称,括号内可以写入参数
上面的网络接口最简单的一种形式,我们先从简单的开始,一步步深入了解。这是一个没有网络参数的get
请求方式,需要在方法头部添加@GET
注解,表示采用get
方法访问网络请求,括号内的是请求的地址(Url的一部分) ,其中返回类型是Call<*>
,*
表示接收数据的类,如果想直接获取ResponseBody
中的内容,可以定义网络请求返回值为Call
,ResponseBody
是请求网络后返回的原始数据,如果网络请求没有参数,不用写。
这里特别说明Url的组成(下面会讲解到),retrofit把网络请求的Url分成两部分设置:第一部分在创建Retrofit实例时通过.baseUrl()设置,第二部分在网络接口注解中设置,比如上面接口的"/user",网络请求的完整地址Url = Retrofit实例.baseUrl()+网络请求接口注解()。
下面我们来看看有参数的get
请求方法:
@GET("user") Call getData2(@Query("id") long idLon, @Query("name") String nameStr);
@Query
请求参数注解,用于Get
请求中的参数"id"/"name"
参数字段,与后台给的字段需要一致long/String
声明的参数类型idLon/nameStr
实际参数
添加参数在方法括号内添加@Query
,后面是参数类型和参数字段,表示后面idLon
的取值作为"id"
的值,nameStr
的取值作为"name"
的值,其实就是键值对,Retrofit
会把两个字段拼接到接口中,追加到"/user"
后面。比如:baseUrl
为https://api.github.com/
,那么拼接网络接口注解中的地址后变为:https://api.github.com/user
,我们需要传入的id=10006,name="刘亦菲"
,那么拼接参数后就是完整的请求地址:https://api.github.com/user?id=10006&name=刘亦菲
。
@GET("user") Call getData3(@QueryMap Map<String, Object> map);
@QueryMap
请求参数注解,与@Query
类似,用于不确定表单参数Map<String, Object> map
通过Map
将不确定的参数传入,相当于多个Query
参数
如果有不确定的把表单参数我们可以使用@QueryMap
注解来添加参数,通过Map
将不确定的参数存入,然后在方法体中带给网络接口,相当于多个Query
参数,看看下面的使用:
Map<String, Object> map = new HashMap<>(); map.put("id", 10006); map.put("name", "刘亦菲"); Call call = retrofit.create(Api.class).getData3(map);
(2)@POST、@FormUrlEncoded、@File、@FileMap、@Body的使用
我们来看看没有参数的post
方法的请求:
@POST("user/emails") Call getPostData();
@POST
请求方法注解,表示采用post
方法访问网络请求,括号后面是部分的URL
地址
post
请求网络方法,需要在方法头部添加@POST
注解,表示采用post
方法访问网络请求,这里是没有参数的,那有参数的Post
请求方法该怎样写呢?
@FormUrlEncoded @POST("user/emails") Call getPostData2(@Field("name") String nameStr, @Field("sex") String sexStr);
@FormUrlEncoded
请求格式注解,请求实体是一个Form
表单,每个键值对需要使用@Field
注解@Field
请求参数注解,提交请求的表单字段,必须要添加,而且需要配合@FormUrlEncoded
使用"name"/"sex"
参数字段,与后台给的字段需要一致String
声明的参数类型nameStr/sexStr
实际参数,表示后面nameStr
的取值作为"name"
的值,sexStr
的取值作为"sex"
的值
Post
请求如果有参数需要在头部添加@FormUrlEncoded
注解,表示请求实体是一个Form
表单,每个键值对需要使用@Field
注解,使用@Field
添加参数,这是发送Post
请求时,提交请求的表单字段,必须要添加的,而且需要配合@FormUrlEncoded
使用
@FormUrlEncoded @POST("user/emails") Call getPsotData3(@FieldMap Map<String, Object> map); Map<String, Object> fieldMap = new HashMap<>(); map.put("name", "刘亦菲"); map.put("sex", "女"); Call psotData3 = retrofit.create(Api.class).getPostData3(map);
@FieldMap
请求参数注解,与@Field
作用一致,用于不确定表单参数Map<String, Object> map
通过Map
将不确定的参数传入,相当于多个Field
参数
当有多个不确定参数时,我们可以使用@FieldMap
注解,@FieldMap
与@Field
的作用一致,可以用于添加多个不确定的参数,类似@QueryMap
,Map
的key
作为表单的键,Map
的value
作为表单的值。
适用于Post
请求的还有一个注解@Body
,@Body
可以传递自定义类型数据给服务器,多用于post
请求发送非表单数据,比如用传递Json
格式数据,它可以注解很多东西,比如HashMap
、实体类等,我们来看看它用法:
@POST("user/emails") Call getPsotDataBody(@Body RequestBody body);
@Body
上传json
格式数据,直接传入实体它会自动转为json
,这个转化方式是GsonConverterFactory
定义的
特别注意:@Body
注解不能用于表单或者支持文件上传的表单的编码,即不能与@FormUrlEncoded
和@Multipart
注解同时使用,否则会报错,如下图:
我们找到@Body
的源码发现,isFormEncoded
为true
或者isMultipart
为ture
时,满足其中任何一个条件都会抛出该异常,通过变量名字可以看出使用@FormUrlEncoded
或者@Multipart
标签。
if (annotation instanceof Body) {
if (isFormEncoded || isMultipart) {
throw parameterError(method, p, "@Body parameters cannot be used with form or multi-part encoding."); } }
(3)@HTTP
@HTTP
注解的作用是替换@GET
、@POST
、@PUT
、@DELETE
、@HEAD
以及更多拓展功能
@HTTP(method = "GET", path = "user/keys", hasBody = false) Call getHttpData();
method
表示请求的方法,区分大小写,这里的值Retrofit
不会再做任何处理,必须要保证正确path
网络请求地址路径hasBody
是否有请求体,boolean
类型
@HTTP
注解可以通过method
字段灵活设置具体请求方法,通过path
设置网络请求地址,用的比较少。@HTTP
替换@POST
、@PUT
、@DELETE
、@HEAD
等方法的用法类似,这里就不一一讲解了。
(4)@Path
@GET("orgs/{id}") Call getPathData(@Query("name") String nameStr, @Path("id") long idLon);
@Query
get
请求方法参数的注解,上面已经解释了,这里就不重复讲@Path
请求参数注解,用于Url
中的占位符{}
,所有在网址中的参数
@Path
注解用于Url
中的占位符{}
,所有在网址中的参数,如上面 @GET("orgs/{id}")
的id
,通过{}
占位符来标记id
,使用@Path
注解传入idLon
的值,注意有的Url
既有占位符又有"?"
后面的键值对,其实@Query
和@Path
两者是可以共用的。在发起请求时,{id}
会被替换为方法中第二个参数的值idLon
。
(5)@Url
如果需要重新地址接口地址,可以使用@Url
,将地址以参数的形式传入即可。如果有@Url
注解时,GET
传入的Url
可以省略。
@GET Call getUrlData(@Url String nameStr, @Query("id") long idLon);
@Url
表示指定请求路径,可以当做参数传入
(6)@Header、@Headers
我们可以在方法参数内添加请求头,@Header
用于添加不固定的请求头,作用于方法的参数,作为方法的参数传入,该注解会更新已有的请求头。
@GET("user/emails") Call getHeaderData(@Header("token") String token);
@header
请求头注解,用于添加不固定请求头
我们想对某个方法添加固定请求头时可以参考下面的写法,@headers
用于添加固定的请求头,作用于方法,可以同时添加多个,通过该注解添加的请求头不会相互覆盖,而是共同存在。
@Headers({
"phone-type:android", "version:1.1.1"}) @GET("user/emails") Call getHeadersData();
@headers
请求头注解,用于添加固定请求头,可以添加多个
(7)@Streaming
@Streaming @POST("gists/public") Call getStreamingBig();
@Streaming
表示响应体的数据用流的方式返回,使用于返回数据比较大,该注解在下载大文件时特别有用
我们在使用下载比较大的文件的时候需要添加@Streaming
注解
(8)@Multipart、@part、@PartMap
@Multipart @POST("user/followers") Call getPartData(@Part("name") RequestBody name, @Part MultipartBody.Part file);
@Multipart
表示请求实体是一个支持文件上传的表单,需要配合@Part
和@PartMap
使用,适用于文件上传@Part
用于表单字段,适用于文件上传的情况,@Part
支持三种类型:RequestBody
、MultipartBody.Part
、 任意类型@PartMap
用于多文件上传, 与@FieldMap
和@QueryMap
的使用类似
上面的使用是一个上传文字和文件的写法,在使用@Part
注解时需要在头部添加@Multipart
注解,实现支持文件上传,我们来看看上面代码怎么使用:
// 声明类型,这里是文字类型 MediaType textType = MediaType.parse("text/plain"); // 根据声明的类型创建RequestBody,就是转化为RequestBody对象 RequestBody name = RequestBody.create(textType, "这里是你需要写入的文本:刘亦菲"); // 创建文件,这里演示图片上传 File file = new File("文件路径"); if (!file.exists()) {
file.mkdir(); } // 将文件转化为RequestBody对象 // 需要在表单中进行文件上传时,就需要使用该格式:multipart/form-data RequestBody imgBody = RequestBody.create(MediaType.parse("image/png"), file); // 将文件转化为MultipartBody.Part // 第一个参数:上传文件的key;第二个参数:文件名;第三个参数:RequestBody对象 MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), imgBody); Call partDataCall = retrofit.create(Api.class).getPartData(name, filePart);
首先声明类型,然后根据类型转化为RequestBody
对象,返回RequestBody
或者转化为MultipartBody.Part
,需要在表单中进行文件上传时,就需要使用该格式:multipart/form-data
。
@PartMap
的使用与@FieldMap
和@QueryMap
的使用类似,用于多文件上传,我们直接看代码:
@Multipart @POST("user/followers") Call getPartMapData(@PartMap Map<String, MultipartBody.Part> map);
File file1 = new File("文件路径"); File file2 = new File("文件路径"); if (!file1.exists()) {
file1.mkdir(); } if (!file2.exists()) {
file2.mkdir(); } RequestBody requestBody1 = RequestBody.create(MediaType.parse("image/png"), file1); RequestBody requestBody2 = RequestBody.create(MediaType.parse("image/png"), file2); MultipartBody.Part filePart1 = MultipartBody.Part.createFormData("file1", file1.getName(), requestBody1); MultipartBody.Part filePart2 = MultipartBody.Part.createFormData("file2", file2.getName(), requestBody2); Map<String,MultipartBody.Part> mapPart = new HashMap<>(); mapPart.put("file1",filePart1); mapPart.put("file2",filePart2); Call<ResponseBody> partMapDataCall = retrofit.create(Api.class).getPartMapData(mapPart);
上面的(@PartMap Map<String, MultipartBody.Part> map
)方法参数中的MultipartBody.Part
可以是RequestBody
、String
、MultipartBody.Part
等类型,可以根据个人需要更换,这里就不一一说明了。
2、Retrofit在项目中简单使用(get请求)
下面我们正式在项目简单使用Retrofit
,需要以下几个步骤:
(1)在清单文件中添加网络权限
Retrofit
是网络请求框架,毫无疑问必须要在AndroidManifest.xml
文件中添加网络权限
<uses-permission android:name="android.permission.INTERNET"/>
(2)添加Retrofit的依赖库
在build.gradle
文件中添加Retrofit
依赖库,由于Retrofit
已经封装有okhttp
所以不需要再添加okhttp
的库
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
(3)创建接受服务器返回数据的类
在正式的请求网络数据中,返回的数据的外嵌套部分都是一样的,携带状态码、状态信息、实体数据一起返回,我们可以将它封装一个统一的数据回调类。
public class Data {
private int code; private String message; private T data; public int getCode() {
return code; } public void setCode(int code) {
this.code = code; } public String getMessage() {
return message; } public void setMessage(String message) {
this.message = message; } public T getData() {
return data; } public void setData(T data) {
this.data = data; } }
(4)创建用于描述网络接口的类
这个类就是Retrofit
将okhttp
请求抽象成java的接口类,用注解描述和配置网络请求参数,封装Url
地址和网络数据请求
public interface Api {
// get请求 @GET("api/rand.music") Call<Data> getJsonData(@Query("sort") String sort, @Query("format") String format); }
在方法头部添加@GET
注解,表示采用get
方法访问网络请求,括号内的是请求的地址(Url
的一部分) ,其中返回类型是Call<*>
,*
表示接收数据的类,Data
是上面封装的一个接收数据的统一公共类,添加参数在方法括号内添加@Query
,后面是参数类型和参数字段,其实就是键值对的形式。
(5)创建Retrofit实例
// 构建Retrofit实例 Retrofit retrofit = new Retrofit.Builder() //设置网络请求BaseUrl地址 .baseUrl("https://api.uomg.com/") //设置数据解析器 .addConverterFactory(GsonConverterFactory.create()) .build();
通过new Retrofit.Builder().buile()
构建Retrofit
实例对象,同时设置baseUrl
地址,Retrofit
把网络请求的URL
分成了两部分设置:
第一部分:在创建Retrofit
实例时通过.baseUrl()
设置,
.baseUrl("https://api.uomg.com/")
就是上面的这个地址会和方法请求注解上面的地址拼接在一起,baseUrl
地址必须以“/”
结尾,否则会报错,
第二部分:在网络请求接口的注解设置,就是在上面的APi
接口中用GET
注解的字符串:
@GET("api/rand.music")
上面地址拼接后的完整地址为:https://api.uomg.com/api/rand.music?sort=&format=
,然后通过addConverterFactory
设置数据解释器,这里添加的是Gson
解释器。这是为了使来自接口的json
结果会自动解析成定义好的字段和类型都相符的json
对象接受类,在Retrofit 2.0
中已经没有Converter
,需要自己创建一个Converter
, 不然Retrofit
只能接收字符串结果,最后需要自己去解析。
在build.gradle
中添加Converter
依赖:
implementation 'com.squareup.retrofit2:converter-gson:2.0.2'
Retrofit
支持多种数据解析,需要在Gradle
添加依赖:
数据解析器 | Gradle依赖 |
---|---|
Gson | com.squareup.retrofit2:converter-gson:2.0.2 |
Jackson | com.squareup.retrofit2:converter-jackson:2.0.2 |
Simple XML | com.squareup.retrofit2:converter-simplexml:2.0.2 |
Protobuf | com.squareup.retrofit2:converter-protobuf:2.0.2 |
Moshi | com.squareup.retrofit2:converter-moshi:2.0.2 |
Wire | com.squareup.retrofit2:converter-wire:2.0.2 |
Scalars | com.squareup.retrofit2:converter-scalars:2.0.2 |
(6)创建网络请求接口实例
// 创建网络请求接口对象实例 Api api = mRetrofit.create(Api.class); // 对发送请求进行封装 Call<Data> dataCall = api.getJsonData("新歌榜", "json");
调用Retrofit
实例对象create()
方法,传入网络接口类,得到接口对象实例,调用接口类中的方法。
(7)发送网络请求(同步/异步)
// 异步请求 dataCall.enqueue(new Callback<Data>() {
// 请求成功回调 @Override public void onResponse(Call<Data> call, Response<Data> response) {
} //请求失败回调 @Override public void onFailure(Call<Data> call, Throwable t) {
} }); //同步请求 Response<Data> data= dataCall.execute();
(8)处理返回的数据
dataCall.enqueue(new Callback<Data>() {
@Override public void onResponse(Call<Data> call, Response<Data> response) {
Toast.makeText(MainActivity.this, "get回调成功:异步执行", Toast.LENGTH_SHORT).show(); Data body = response.body(); if (body == null) return; Info info = body.getData(); if (info == null) return; mTextView.setText("返回的数据:" + "\n\n" + info.getName() + "\n" + info.getPicurl()); } @Override public void onFailure(Call<Data<Info>> call, Throwable t) {
Log.e(TAG, "回调失败:" + t.getMessage() + "," + t.toString()); Toast.makeText(MainActivity.this, "回调失败", Toast.LENGTH_SHORT).show(); } });
对异步返回的数据进行处理,网络加载完成。get
的请求地址为:https://api.uomg.com/api/rand.music?sort=%E6%96%B0%E6%AD%8C%E6%A6%9C&format=json
效果如下图所示:
执行get
请求按钮,代码执行get
请求,一段时间后返回了请求的结果。
3、post请求的简单使用
参照上面get
请求的实例,重复步骤部分就不一一再讲了,我们来看一下不同样的部分:
创建网络请求接口:
public interface Api {
// post请求,如果有参数需要添加 @FormUrlEncoded注解,即和@Field配合使用 @FormUrlEncoded @POST("api/comments.163") Call postDataCall(@Field("format") String format); }
调用网络请求接口实例,处理返回数据:
// 步骤4:创建Retrofit对象 Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.uomg.com/") // 设置网络请求baseUrl .addConverterFactory(GsonConverterFactory.create()) //设置使用Gson解析 .build(); // 步骤5:创建网络请求接口的实例 Api request = retrofit.create(Api.class); // 步骤6:对发送请求进行封装: Call<Translation> call = request.postDataCall("JSON"); // 步骤7:发送网络请求(异步) call.enqueue(new Callback<Translation>() {
// 请求成功时回调 @Override public void onResponse(Call<Translation> call, Response<Translation> response) {
// 步骤8:请求处理,输出结果 Object body = response.body(); if (body == null) return; mTextView.setText("返回的数据:" + "\n\n" + response.body().toString()); Toast.makeText(MainActivity.this, "post回调成功:异步执行", Toast.LENGTH_SHORT).show(); } // 请求失败时回调 @Override public void onFailure(Call<Translation> call, Throwable throwable) {
Log.e(TAG, "post回调失败:" + throwable.getMessage() + "," + throwable.toString()); Toast.makeText(MainActivity.this, "post回调失败", Toast.LENGTH_SHORT).show(); } });
对异步返回的数据进行处理,网络加载完成。post的请求地址和参数打印如下:
数据打印效果如下:
执行post
请求按钮,代码执行post
请求,一段时间后返回了请求的结果。
源码地址
今天的文章 Android Retrofit2 详解和使用分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/102194.html