整体Retrofit内容如下:
- 1、Retrofit解析1之前哨站——理解RESTful
- 2、Retrofit解析2之使用简介
- 3、Retrofit解析3之反射
- 4、Retrofit解析4之注解
- 5、Retrofit解析5之代理设计模式
- 6、Retrofit解析6之面向接口编程
- 7、Retrofit解析7之相关类解析
- 8、Retrofit解析8之核心解析——ServiceMethod及注解1
- 9、Retrofit解析8之核心解析——ServiceMethod及注解2
- 10、Retrofit解析9之流程解析
- 11、Retrofit解析10之感谢
前面介绍完RESTful之后,我们先来初步认识下Retrofit的使用"姿势"。本文的主要内容如下:
- 1、Retrofit是什么?
- 2、Retrofit的配置
- 3、Retrofit的那些注解
- 4、为什么要用Retrofit?
- 5、Form表单提交与multipart/form-data
- 6、Retrofit2 对multipart/form-data的支持
- 7、总结
一、什么是Retrofit
Retrofit的官网是这样说的:
A type-safe HTTP client for Android and Java
我简单翻译一下就是:
一个类型安全的、Android或者Java的客户端
通过使用注解去描述一个HTTP请求,并且支持Multipart请求和文件上传。
我理解的Retrfit:
一个可以简化我们网络操作的工作的第三方库。当然我们自己也可以实现,但是自己去实现带来的是比较高的时间成本和检验成本。同样,Retrofit是Square公司开源的一个高质量高效率的HTTP库,它将我们自己开发的底层的代码和细节都封装了起来,有了Retrofit之后我们对于一些请求我们就只需要一行代码或者一个注解。所有的网络通信,其核心任务就只有一个就是:Client端与Server端进行数据和交互操作,所有Retrofit就将底层代码都封装起来,只是暴露除了我们业务中的数据模型和操作方法。下面让我们了解下Retrofit的配置
二、Retrofit的配置
1、首选在你的项目build.gradle里面添加如下配置
compile 'com.squareup.retrofit2:retrofit:2.2.0'
如果你序列化 采用GSON,同时回调采用RxJava 处理,还应该添加如下内容
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
当然也别忘记添加网络权限
2、定义返回值实体类
这个类煲剧哦返回数据的所有属性
public class GitHubRepo {
private int id;
private String name;
public GitHubRepo() {
}
public int getId() {
return id;
}
public String getName() {
return name;
}
}
3、定义网络接口
写一个interface名字是GitHubService
public interface GitHubService {
@GET("users/{user}/repos")
Call> listRepos(@Path("user") String user);
}
4、创建Retrofit对象
retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.client(client)//这个client是OkHttpClient,以后和Okhttp的基本用法和流程分析中细说
// .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(new Gson()))
.build();
5、创建Service对象
GitHubService service=retrofit.create(GitHubService.class);
6、发起请求
Call> repos = service.listRepos("octocat");
三、注解详解
Retrofit 2.3 包含很多的注解,包括如下内容:
- 1、方法注解:@GET @POST、@PUT、@DELETE、@PATCH、@OPTIONS、@HTTP
- 2、标记注解:@FormUrlEncoded、@Multipart、@Streaming
- 3、参数注解:@Query 、@QueryMap、@Body、@Field、@FieldMap、@Part、@PartMap
- 4、其它注解:@Path、@Header、@Headers、@Url
(一)、方法注解:
1、@GET:用于发送一个get请求
@GET 注解一般必须添加相对路径或者绝对路径或者全路径,如果不想用在@GET 注解后添加请求路径,则可以在方法的第一个参数中用@Url 添加请求路径。
2、@POST:用于发送一个POST请求
@POST 注解一般必须添加相对路径或绝对路径或者全路径,如果不想在@POST 后添加请求路径,则可以在方法的第一个参数用@Url 注解添加请求路径。
3、@PUT:用于发送一个PUT请求
@PUT 注解一般必须添加相对路径或者绝对路径或者全路径,如果不想在PUT注解后添加请求路径,则可以在方法的第一个参数用@Url 注解添加请求路径。
4、@DELETE:用于发送一个DELETE请求
@DELETE 注解 一般必须添加相对路径或者绝对路径或者全路径,如果不想在DELETE注解后添加请求路径,则可以在方法的第一个参数中用@Url 注解添加请求路径。
5、@PATCH:用于发送一个PATCH请求
@PATCH 注解 一般必须添加相对路径或绝对路径或者全路径,如果不想在PATCH注解后添加请求路径,则可以在方法的第一恶参数用@Url 注解添加请求路径
6、@OPTIONS:用于发送一个OPTIONS请求
@OPTIONS 注解一般必须添加相对路径或绝对路径或者全路径,如果不想在OPTIONS注解后添加请求路径,则可以在方法的第一个参数用@Url 注解添加请求路径。
7、@HTTP:作用于方法,用于发送一个自定义的HTTP
如下所示:
//自定义HTTP请求的标准样式
interface Service {
@HTTP(method = "CUSTOM", path = "custom/endpoint/")
Call customEndpoint();
}
//发送一个DELETE请求
interface Service {
@HTTP(method = "DELETE", path = "remove/", hasBody = true)
Call deleteObject(@Body RequestBody object);
}
(二)、标记注解:
1、@FormUrlEncoded:用于修饰Fiedl注解 和FileldMap注解
使用该注解,表示请求正文将使用表单网址编码。字段应该声明为参数,并用@Field 注解和 @FieldMap 注解,使用@FormUrlEncoded 注解的请求将具有"application/x-www-form-urlencoded" MIME类型。字段名称和值将先进行UTF-8进行编码,再根据RFC-3986进行URI编码。
2、@Multipart:作用于方法
使用该注解,表示请求体是多部分的,每个部分作为一个参数,且用Part注解声明。
3、@Streaming:作用于方法
未使用@Straming 注解,默认会把数据全部载入内存,之后通过流获取数据也是读取内存中数据,所以返回数据较大时,需要使用该注解。
处理返回Response的方法的响应体,用于下载大文件
@Streaming
@GET
Call downloadFileWithDynamicUrlAsync(@Url String fileUrl);
提醒:如果是下载大文件必须加上@Streaming 否则会报OOM
(三)、参数注解
1、@Query:作用于方法参数,用于添加查询参数,即请求参数
参数值通过String.valueOf()转换为String 并进行URL编码,使用该注解定义的参数,参数值可以为空,为空时,忽略该值,当传入一个List或array时,为每个非空item拼接请求键值对,所有的键是统一的,如:name=张三&name=李四&name=王五。
@GET("/list")
Call list(@Query("page") int page);
@GET("/list")
Call list(@Query("category") String category);
//传入一个数组
@GET("/list")
Call list(@Query("category") String... categories);
//不进行URL编码
@GET("/search")
Call list(@Query(value="foo", encoded=true) String foo);
2、@QueryMap:作用于方法的参数
以map的形式添加查询参数,即请求参数,参数的键和值都通过String.valueOf()转换为String格式。默认map的值进行URL编码,map中的每一项发键和值都不能为空,否则跑出IllegalArgumentException异常。
示例如下:
//使用默认URL编码
@GET("/search")
Call list(@QueryMap Map filters);
//不使用默认URL编码
@GET("/search")
Call list(@QueryMap(encoded=true) Map filters);
3、@Body:作用于方法参数
使用@Body 注解定义的参数不能为null
当你发送一个post或put请求,但是又不想作为请求参数或表单的方式发送请求时,使用该注解定义的参数可以直接传入一个实体类,retrofit会通过convert把该实体序列化并将序列化的结果直接作为请求体发送出去。
示例如下:
先来看下实体类
class Repo {
final String owner;
final String name;
Repo(String owner, String name) {
this.owner = owner;
this.name = name;
}
}
再来看下接口类
interface Service {
@POST("/")
Call sendNormal(@Body Repo repo);
4、@Field:作用于方法的参数
用String.valueOf()把参数值转换为String,然后践行URL编码,当参数值为null是=时,会自动忽略,如果传入的是一个List或者array,则为每一个非空的item拼接一个键值对,每一个键值对中的键是相同的,值就是非空的item的值。如:name=张三&name=李四&name=王五,如果itme的值有空格,在拼接的时候会自动忽略,例如某个item的值为:张 三,则拼接后为name=张三。
示例如下:
//普通参数
@FormUrlEncoded
@POST("/")
Call example(@Field("name") String name,@Field("occupation") String occupation);
//固定或可变数组
@FormUrlEncoded
@POST("/list")
Call example(@Field("name") String... names);
5、@FieldMap:作用于方法的参数
map中的每一项的键和值都不能为空,否则抛出IllegalArgumentException异常。
如下:
@FormUrlEncoded
@POST("/things")
Call things(@FieldMap Map fields);
6、@Part:作用于方法的参数,用于定义Multipart请求的每和part
使用该注解定义的参数,参数值可以为空,为空时,则忽略。使用该注解定义的参数类型有如下3中方式可选:
- 1 okhttp2.MulitpartBody.Part,内容将被直接使用。省略part中的名称,即@Part MultipartBody.Part part
- 2 如果类型是RequestBody,那么该值直接与其内容类型一起使用。在注释中提供part名称(例如,@Part("foo") RequestBody foo)
- 3 其它对象类型将通过使用转换器转换为适当的格式。在注释中提供part名称(例如,@Part("foo") Image photo)。
示例如下:
@Multipart
@POST("/")
Call example(
@Part("description") String description,
@Part(value = "image", encoding = "8-bit") RequestBody image);
7、@PartMap:作用于方法的参数
以map的方式定义Multipart请求的每个part map中每一项的键和值都不能为空,否则抛出IllegalArgumentException异常。
使用@PartMap 注解定义的参数类型有一下两种:
- 1 如果类型是RequestBody,那么该值将直接与其内容类型与其使用。
- 2 其它对象类型将通过使用转换器转换为适当的格式。
(四)其他注解:
1、@Path:用于方法的参数
在URL路径中替换指定参数值。使用String.valueOf()和URL编码将值转换为字符串。
使用@Path 注解 定义的参数的值不能为空,参数值默认使用URL编码。
2、@Header:作用于方法的参数,用于添加请求头
使用 @Header 注解 定义的请求头可以为空,当为空时,会自动忽略,当传入一个List或者array时,为拼接每个非空的item的值到请求头中。
具有相同名称的请求头不会相互覆盖,而是照样添加到请求头中
代码如下:
@GET("/")
Call foo(@Header("Accept-Language") String lang);
3、@Headers:作用于方法,用于添加一个或多个请求头中
具有相同名称的请求头不会相互覆盖,而是会照样添加到请求头中。
示例如下:
//添加一个请求头
@Headers("Cache-Control: max-age=640000")
@GET("/")
//添加多个请求头
@Headers({ "X-Foo: Bar", "X-Ping: Pong"})
@GET("/")
4、@Url: 作用于方法参数
用于添加请求的接口地址:
代码如下:
@GET
Call list(@Url String url);
(四)注意事项:
- 1、Map用来组合复杂的参数,并且对于FieldMap,HeaderMap,PartMap,QueryMap这四种作用方法的注解,其参数类型必须为Map实例,且key的类型必须为String类型,否则抛出异常。
- 2、Query、QueryMap与Field、FieldMap功能一样,生成的数据形式一样;Query、QueryMap的数据体现在Url上;Field、FieldMap的数据是请求体
- 3、{占位符}和PATH尽量只用在URL的path部分,url的参数使用Query、QueryMap代替,保证接口的简洁
- 4、Query、Field、Part支持数据和实现了iterable接口的类型,如List、Set等,方便向后台传递数组,代码如下:
- 5、以上部分注解真正的实现在ParameterHandler类中,每个注解的真正实现都是ParameterHandler类中的一个final类型的内部类,每个内部类都对各个注解的使用要求做了限制,比如参数是否可空、键和值是否可空等。
- 6、@FormUrlEncoded 注解和@Multipart 注解不能同时使用,否则会抛出methodError(“Only one encoding annotation is allowed.”),可在ServiceMethod类中parseMethodAnnotation()方法中找到不能同时使用的具体原因。
- 7、@Path 与@Url 注解不能同时使用,否则会抛出parameterError(p, "@Path parameters may not be used with @Url."),可在ServcieMethod类中parseParameterAnnotation()方法中找到不能同时使用的具体代码。其实原因也是很好理解:Path注解用于替换url中的参数,这就要求在使用path注解时,必须已经存在请求路径。不然没法替换路径中指定的参数。而@Url 注解是在参数中指定了请求路径的,这时候情定请求路径已经晚,path注解找不到请求路径,更别提更换请求路径了中的参数了。
- 8 使用@Body 注解的参数不能使用form 或multi-part编码,即如果为方法使用了FormUrlEncoded或Multipart注解,则方法的参数中不能使用@Body 注解,否则会抛出异常parameterError(p, “@Body parameters cannot be used with form or multi-part encoding.”)
四、为什么要用Retrofit
1、咱们一起来看下Retrofit的优点:
- 请求的方法参数注解可以定制
- 支持同步、异步和RxJava
- 超级解耦(我最爱)
- 可以配置不同的反序列化工具来解析数据,如json、xml等
2、为什么使用Retrofit?
在处理HTTP请求的时候,因为不同场景或者边界情况等比较难处理。你需要考虑网络状态,需要在请求失败后重试,需要处理HTTPS等问题,二这些事情让你很苦恼,而Retrofit可以将你从这些头疼的事情中解放出来。
当然你也可以选择android-async-http和Volley,但为什么选择Retrofit?首先效率高,其次Retrofit强大且配置灵活,第三和OkHttp无缝衔接,第四Jack Wharton主导的(你懂的)。
在Retrofit2之前,OkHttp是一个可选的客户端。二Retrofit2中,Retrofit与OkHttp强耦合,使得更好地利用OkHttp,包括使用OkHttp解决一些棘手的问题。
五、Form表单提交与multipart/form-data
由于后面涉及到Form表单提交数据的格式,为了方便部分人更好的理解,我先在这里讲解下。
(一)、Form表单
1、form表单常用属性
- action:url 地址,服务器接收表单数据的地址
- method:提交服务器的http方法,一般为post和get
- name:最好好吃name属性的唯一性
- enctype: 表单数据提交时使用的编码类型,默认使用"pplication/x-www-form-urlencoded",如果是使用POST请求,则请求头中的content-type指定值就是该值。如果表单中有上传文件,编码类型需要使用"multipart/form-data",类型,才能完成传递文件数据。
2、浏览器提交表单时,会执行如下步骤
- 1、识别出表单中表单元素的有效项,作为提交项
- 2、构建一个表单数据集
- 3、根据form表单中的enctype属性的值作为content-type对数据进行编码
- 4、根据form表单中的action属性和method属性向指定的地址发送数据
3、提交方式
- 1、get:表单数据会被encodeURIComponent后以参数的形式:name1=value1&name2=value2 附带在url?后面,再发送给服务器,并在url中显示出来。
- 2、post:content-type 默认"application/x-www-form-urlencoded"对表单数据进行编码,数据以键值对在http请求体重发送给服务器;如果enctype 属性为"multipart/form-data",则以消息的形式发送给服务器。
4、POST请求
HTTP/1.1 协议规定的HTTP请求方法有OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE、CONNECT 这几种。其中POST一般用于向服务器提交数据。
大家知道,HTTP协议是以ASCII 码传输,建立在TCP/IP协议之上的应用层规范。规范把HTTP请求分为3大块:状态行、请求头、消息体。类似于如下:
协议规定POST提交的数据必须放在消息主题(entity-body)中,但协议并没有规定数据必须使用什么编码方式。实际上,开发者可以自己决定消息体的格式,只要后面发送的HTTP请求满足上面的格式就可以了。
但是,数据发送出去后,还要服务器解析成功才有意义。一般服务器都内置了自动解析常见数据格式的功能。服务端通常是根据请求头(headers)中的Content-Type字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析。所以说到POST提交数据方法,包含了Content-Type和消息主题编码方式两部分。
5、enctype 指定的 content-type
- application/x-www-form-urlencoded
- application/json
- text/xml
- multipart/form-data
下面我们就详细的介绍下它们。
(一)、application/x-www-form-urlencoded
这应该是最常见的POST提交数据的方式了。浏览器的原生