网络请求在 Android 中的使用非常多,在上一篇文章中我们简单的介绍了 OkHttp 的基本使用,今天我们来入门一下 Retrofit 网络请求库,在 github 上到目前为止,Retrofit 的 Star 数量已经到了 22 k,Fork 4.6 k,看到这里我们就能感觉到 Retrofit 的火热程度,所以我们有必要来认真了解一下 Retrofit 的使用
一、概述
随着 Volley 的逐渐没落,OkHttp 使用的越来越火爆,但是 Retrofit 却对 OkHttp 的依赖性很强,Retrofit 是由 Square 公司出品的针对 Android 和 Java 的网络请求客户端,其实看源码会发现,其实质上就是对 OkHttp 的封装,使用面向接口的方式进行网络请求,利用动态生成的代理类封装了网络请求的底层,Retrofit 专注于封装接口完成业务需求,OkHttp 专注网络请求的高效安全,具体来说就是应用程序通过 Retrofit 来发起网络请求,实际上是使用 Retrofit 接口层封装请求参数信息,如 url、header 等,之后由OkHttp 完成后续的请求操作,服务端返回数据之后,OkHttp 将结果交给 Retrofit,Retrofit 对结果进行相应的处理,还有很重要的就是 Retrofit 提供了对 RxJava 的支持
Retrofit GitHub 地址:https://github.com/square/retrofit
官网地址:http://square.github.io/retrofit/
二、Retrofit 2.0 与之前版本的区别
既然我们的题目是 Retrofit 2.0,那么想必 2.0 和之前的版本可能有一些不同,接下来我们就来看看有哪些具体不同的地方:
- Retrofit 1 中使用的是 RestAdapter,而到了 2.0 中使用的是 Retrofit 实例
- setEndpoint 改为 baseUrl
- Retrofit 1 中使用 setRequestInterceptor 设置拦截器,对 http 请求进行相应的处理
- Retrofit 2.0 通过 OkHttp 的拦截器拦截 http请求进行监控,重写或重试,日志打印
- converter,Retrofit1中的 setConverter,换以 addConverterFactory,用于支持 Gson 的装换
三、 Retrofit 使用详解
1. 在 Gradle 中添加依赖库,这里别忘了接着在 AndroidManifest.xml 清单文件中添加一下网络权限
compile 'com.squareup.retrofit2:retrofit:2.3.0'
<uses-permission android:name="android.permission.INTERNET"/>
2. 创建接受服务器数据的类
说明一下,我们这里是模拟来说明情况,具体到项目中还要以自己公司服务器返回的具体数据为准
/**
* 接受服务器返回的数据类(模拟)
* Created by qiudengjiao on 2017/8/2.
*/
public class User {
private String userName;
private String password;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
/**
* 用于描述网络请求的接口
* Created by qiudengjiao on 2017/8/1.
*/
public interface ApiService {
// @GET:采用Get方法发送网络请求
@GET
Call<User> getUser();
// getCall()是自定义的接收网络请求数据的方法
// 其中返回类型为Call<*>,*是接收数据的类,这里我们定义为User类
// 如果想直接获得Responsebody中的内容,可以定义网络请求返回值为Call<ResponseBody>
}
4. Retrofit 注解类型
4.1 方法注解:
@GET、@POST、@PUT、@DELETE、@PATH、@HEAD、@OPTIONS、@HTTP
@GET、@POST、@PUT、@DELETE、@HEAD 对应于网络请求的方式,例如:
/**
* 用于描述网络请求的接口
* Created by qiudengjiao on 2017/8/1.
*/
public interface ApiService {
// @GET:采用Get方法发送网络请求
@GET
Call<User> getUser();
// @POST:采用POST方法发送网络请求
@POST
Call<User> postUser();
// @PUT:采用PUT方法发送网络请求
@PUT
Call<User> putUser();
// @DELETE:采用DELETE方法发送网络请求
@DELETE
Call<User> deleteUser();
}
@HTTP 可以替代其他方法的任意一种:
/**
* method 表示请求的方法,不区分大小写
* path表示路径
* hasBody表示是否有请求体
*/
@HTTP(method = "get", path = "users/{user}", hasBody = false)
Call<ResponseBody> getFirstBlog(@Path("user") String user);
这个注解比较特殊一点,@HTTP 可以替换@GET、@POST、@PUT、@DELETE、@HEAD 注解的作用,还可以用于更多功能的扩展,并且可以通过 method、path、hasBody 这些属性进行设置
4.2 标记注解:
@FormUrlEncoded、@Multipart、@Streaming
@FormUrlEncoded 作用:发送 form-encoded 的数据,每个键值对需要用 @Filed 来注解键名。随后的对象需要提供值
/**
*表明是一个表单格式的请求(Content-Type:application/x-www-form-urlencoded)
* <code>Field("username")</code> 表示将后面的 <code>String name</code> 中 name 的取值作为 username 的值
*/
@POST("/form")
@FormUrlEncoded
Call<ResponseBody> testFormUrlEncoded(@Field("username") String name, @Field("age") int age);
@Multipart 作用:适用于有文件上传,发送 form-encoded 的数据的场景
@POST("/form")
@Multipart
Call<ResponseBody> testFileUpload(@Part("name") RequestBody name, @Part("age") RequestBody age, @Part MultipartBody.Part file);
@Streaming 作用:用于下载大文件
@Streaming
@GET
Call<ResponseBody> downloadFile(@Url String fileUrl);
4.3 参数注解:
@Query、@QueryMap、@Body、@Field、@FieldMap、@Part、@PartMap
@Query、@QueryMap 作用:用来查询参数,用于 GET 方法查询,需要注意,@QueryMap 可以约定是否需要 encode
@Body作用:用于POST请求体,将实例对象根据转换方式转换为对应的 json 字符串参数,这个转化方式是 GsonConverterFactory定义的
@Field、@FieldMap 作用:Post 方式传递简单的键值对,需要添加 @FormUrlEncoded 表示表单提交
@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
@Part、@PartMap 作用:用于 POST 文件上传,其中 @Part MutipartBody.Part 代表文件,@Part("key")RequestBody 代表参数,需要添加 @Multipart 表示支持文件上传的表单
@Multipart
@POST("upload")
Call<ResponseBody> upload(@Part("description") RequestBody description,@Part MultipartBody.Part file);
@Path、@Header、@Headers、@Url
@Path作用:URL 占位符,用于替换和动态更新,相应的参数必须使用相同的字符串被 @Path 进行注释
@GET("group/{id}/users")
Call<User> getUser(@Path("id") int groupId);
//--> http://baseurl/group/groupId/users
//等同于:
@GET
Call<User> getUser(@Url String url);
@Header作用:header 处理,不能被相互覆盖,用于修饰参数
//动态设置Header值
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
等同于:
//静态设置Header值
@Headers("Authorization: authorization")//这里authorization就是上面方法里传进来变量的值
@GET("widget/list")
Call<User> getUser()
@Headers作用:用于修饰方法,用于设置多个 Header 的值:
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);
@Url作用:直接传入 URL 变量 用于设置 URL
到这里 Retrofit 注解我们就说完了,但还有一个比较重要的也是我们需要注意得点,这里我们要说明一下 Retrofit 中 URL 的组成,下面我们拿官网的一段代码来具体讲解:
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
这些注解都有一个参数 value,用来配置其路径,比如示例中的 users/{user}/repos,我们还注意到在构造 Retrofit 之时我们还传入了一个 baseUrl(“https//api.github.com/”),请求的完整 Url 就是通过 baseUrl 与注解的 value(下面的"path") 整合起来的,具体整合的规则如下:
1)path 是绝对路径的形式:
path = "/apath",baseUrl = "http://host:port/a/b"
Url = "http://host:port/apath"
2)path 是相对路径,baseUrl 是目录形式:
path = "apath",baseUrl = "http://host:port/a/b"
Url = "http://host:port/a/b/apath"
3)path 是相对路径,baseUrl 是文件形式:
path = "apath",baseUrl = "http://host:port/a/b"
Url = “http://host:port/a/apath”
建议用第二种方式来配置,并尽量使用同一种路径形式,这里要特别注意,防止混合采用多种配置形式出现一堆 bug
4. 创建 Retrofit 实例:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://www.baidu.com/") // 设置网络请求的Url地址
.addConverterFactory(GsonConverterFactory.create()) // 设置数据解析器
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 支持RxJava平台
.build();
这里主要是设置网络请求的地址,数据解析器以及设置支持 RxJava,数据解析器(converters)Google 官方提供了如下几种:
compile 'org.ligboy.retrofit2:converter-fastjson-android:2.1.0'
Retrofit.Builder retrofit = new Retrofit.Builder();
retrofit.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(FastJsonConverterFactory.create())
.baseUrl("")
.build();
// 创建 网络请求接口 的实例
ApiService apiService = retrofit.create(ApiService.class);
//对 发送请求 进行封装
Call<User> call = apiService.getUser();
//发送网络请求(异步)
call.enqueue(new Callback<User>() {
//请求成功时回调
@Override
public void onResponse(Call<User> call, Response<User> response) {
//请求处理,输出结果
response.body().getUserName();
response.body().getPassword();
}
//请求失败时回调
@Override
public void onFailure(Call<User> call, Throwable t) {
}
});
//创建Retrofit.Builder实例
Retrofit.Builder retrofitBuilder = new Retrofit.Builder();
//创建Retrofit实例
Retrofit retrofit = retrofitBuilder
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(FastJsonConverterFactory.create())
.baseUrl("https://www.baidu.com/")
.build();
// 创建 网络请求接口 的实例
ApiService apiService = retrofit.create(ApiService.class);
//对 发送请求 进行封装
Call<User> call = apiService.getUser();
//发送网络请求(异步)
call.enqueue(new Callback<User>() {
//请求成功时回调
@Override
public void onResponse(Call<User> call, Response<User> response) {
//请求处理,输出结果
response.body().getUserName();
response.body().getPassword();
}
//请求失败时回调
@Override
public void onFailure(Call<User> call, Throwable t) {
}
});