首先呢,刚开始接触retrofit,有很多都是从其他博客那整理过来的,如果有不正确的地方,欢迎各位大佬指正。
Retrofit是什么?
简单的说就是一个网络请求的适配器,它将一个基本的Java接口通过动态代理的方式翻译成一个HTTP请求,并通过OkHttp去发送请求。Retrofit底层是使用OKHttp封装的。准确来说,网络请求的工作本质上是OkHttp完成,而 Retrofit 仅负责网络请求接口的封装。它的一个特点是包含了特别多注解,方便简化你的代码量,此外它还具有强大的可扩展性,支持各种格式转换以及RxJava。
Retrofit的好处?
- 解耦
我们在请求接口数据的时候,API接口定义和API接口使用总是相互影响,什么传参、回调等,耦合在一块。有时候我们会考虑一下怎么封装我们的代码让这两个东西不那么耦合,这个就是Retrofit的解耦目标,也是它的最大的特点。
- 可以配置不同HttpClient来实现网络请求,如OkHttp、HttpClient...
- 支持同步、异步和RxJava
- 可以配置不同的反序列化工具来解析数据,如json、xml...
- 请求速度快,使用非常方便灵活
Retrofit注解
请求注解:
注解代码 | 请求格式 |
@GET | GET请求 |
@POST | POST请求 |
@DELETE | DELETE请求 |
@HEAD | HEAD请求 |
@OPTIONS | OPTIONS请求 |
@PATCH | PATCH请求 |
请求参数:
注解代码 | 说明 |
@Headers | 添加请求头 |
@Path | 替换路径 |
@Query | 替代参数值,通常是结合get请求的 |
@FormUrlEncoded | 用表单数据提交 |
@Field | 替换参数值,是结合post请求的 |
下面我们详细说说这些注解:
一、首先添加依赖,添加网络权限,创建接收服务器返回数据的类:
public class News {
// 根据返回数据的格式和数据解析方式(Json、XML等)定义
...
}
二、创建用于描述网络请求的接口
public interface APi{
// @GET注解的作用:采用Get方法发送网络请求
// getNews(...) = 接收网络请求数据的方法
// 其中返回类型为Call<News>,News是接收数据的类(即上面定义的News类)
// 如果想直接获得Responsebody中的内容,可以定义网络请求返回值为Call<ResponseBody>
@Headers("apikey:81bf9da930c7f9825a3c3383f1d8d766")
@GET("word/word")
Call<News> getNews(@Query("num") String num,@Query("page")String page);
}
①Retrofit将Http请求抽象成Java接口,并在接口里面采用注解来配置网络请求参数。用动态代理将该接口的注解“翻译”成一个Http请求,最后再执行 Http请求
注意: 接口中的每个方法的参数都需要使用注解标注,否则会报错
②APi接口中的最后一个注释,Responsebody是Retrofit网络请求回来的原始数据类,没经过Gson转换什么的,如果你不想转换,比如我就想看看接口返回的json字符串,那就像注释中说的,把Call的泛型定义为ResponseBody:Call<ResponseBody>
③GET注解:说白了就是我们的GET请求方式。
这里涉及到Retrofit创建的一些东西,Retrofit在创建的时候,有一行代码:
baseUrl("http://apis.baidu.com/txapi/")
这个http://apis.baidu.com/txapi/是我们要访问的接口的BaseUrl,而我们现在用GET注解的字符串 "word/word"会追加到BaseUrl中变为:http://apis.baidu.com/txapi/world/world
④@Query
@Query("num")String num, @Query("page")String page;
就是键值对,Retrofit会把这两个字段一块拼接到接口中,追加到 http://apis.baidu.com/txapi/world/world后面,变为 http://apis.baidu.com/txapi/world/world?num=10&page=1,这样,这个带着响应头的接口就是我们最终请求网络的完整接口。
补充一点哈,GET请求方式,如果携带的参数不是以"?num=10&page=1"拼接到接口中(就是不带?分隔符),那就不用Query注解了,而是使用Path注解:
public interface NewsServicr{
@Get("News/{newsId}")
Call<News> getNews(@Path("newsId") String newsId);
}
newId,就用@Path注解在传入newsId的值。
@Query与@Path功能相同,但区别明显不一样。像@Query的例子,我如果使用@Path来注解,那么程序就会报错。这块要搞清楚!
还有一点,有的url既有“{}”占位符,又有“?”后面的键值对(key-value),那Retrofit既得使用@Query注解又得使用@Path注解,也就是说,两者可以同时使用。
⑤@Headers
@Headers("apikey:81bf9da930c7f9825a3c3383f1d8d766")
这个很好理解,这个接口需要添加的header:
apikey:81bf9da930c7f9825a3c3383f1d8d766
@Headers就是把接口的header注解进去。
还有很多添加header的方式,比如:
public interface APi {
@GET("word/word")
Call<News> getNews(@Header("apikey")String apikey, @Query("num")String num, @Query("page")String page);
}
这个就是在代码中动态的添加header,用法如下:
Call<News> news = mApi.getNews("81bf9da930c7f9825a3c3383f1d8d766", "1", "10");
这里再补充一点:@Header与@Headers的区别
举个例子:
//@Header
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
//@Headers
@Headers("Authorization:authorization")
@GET("user")
Call<User> getUser()
以上两个方法的效果是一致的。
区别就在于使用场景和使用方式
使用场景:@Header用于添加不固定的请求头,@Headers用于添加固定的请求头
使用范围:@Header作用于方法的参数;@Headers作用于方法
三、创建Retrofit对象:
Retrofit retrofit = new Retrofit.Builder()
//设置数据解析器
.addConverterFactory(GsonConverterFactory.create())
//设置网络请求的Url地址
.baseUrl("http://apis.baidu.com/txapi/")
.build();
// 创建网络请求接口的实例
APi mApi = retrofit.create(APi.class);
①此处特意说明一下这个网络请求的URL的组成:Retrofit把网络请求的URL 分成了两部分设置:
第一部分:在创建Retrofit实例时通过.baseUrl()设置: "baseUrl("http://apis.baidu.com/txapi/")"
第二部分:在网络请求接口的注解设置,就是在上面的APi接口中用GET注解的字符串:@Get("word/word")
Retrofit的网络请求的完整Url = 创建Retrofit实例时通过.baseUrl()设置的url + 网络请求接口的注解设置
②关于数据解析器(Converter)
//设置数据解析器
.addConverterFactory(GsonConverterFactory.create())
这个有啥用?这句话的作用就是使得来自接口的json结果会自动解析成定义好了的字段和类型都相符的json对象接受类。在Retrofit 2.0中,Package 中已经没有Converter了,所以,你需要自己创建一个Converter, 不然的话Retrofit只能接收字符串结果,你也只能拿到一串字符,剩下的json转换的活还得你自己来干。所以,如果你想接收json结果并自动转换成解析好的接收类,必须自己创建Converter对象,然后使用addConverterFactory把它添加进来!
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 |
我们的实例中
就是使用了第一种Gson数据解析器。
③再来引入另一个方法:addCallAdapterFactory()
上文代码中没有这个方法,但是得知道它的作用,有必要作为补充知识点
看一下我们的接口返回:
Call<News> news = mApi.getNews("1", "10");
返回的Call<News>可以理解成源生的了,默认就这么写。但像很多很多项目都是结合着RXJava来使用这个Retrofit的,那么这个接口返回就会被定义为(伪代码):
Observable<News> news = mApi.getNews("1", "10").subscribeOn(...).observeOn(...);
它返回的是一个Observable类型(观察者模式)。从上面可以看到,Retrofit接口的返回值可以分为两部分,第一部分是返回值类型:Call或者Observable,另一部分是泛型:News
addCallAdapterFactory()影响的就是第一部分:Call或者Observable。Call类型是Retrofit默认支持的(Retrofit内部有一个DefaultCallAdapterFactory),所以你如果不用RXJava + Retrofit结合使用,那就自动忽略掉这个方法,而如果你想要支持RXJava(就是想把返回值定义为Observable对象),就需要我们自己用addCallAdapterFactory()添加:
addCallAdapterFactory(RxJavaCallAdapterFactory.create())
像我们项目中Retrofit创建的代码就是:
retrofit = new Retrofit.Builder()
.baseUrl(URL.SERVICE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
同理,Retrofit不光支持多种数据解析器,也支持多种网络请求适配器:Guava、Java8、RXJava ,使用时也需要在Gradle添加依赖:
网络请求适配器 | Gradle依赖 |
Guava | com.squareup.retrofit2:adapter-guava:2.0.2 |
Java8 | com.squareup.retrofit2:adapter-java8:2.0.2 |
RXJava | com.squareup.retrofit2:adapter-rxjava:2.0.2 |
示例代码
就是用的RXJava。
四、发起网络请求
//对发送请求进行封装
Call<News> news = mApi.getNews("1", "10");
//发送网络请求(异步)
news.enqueue(new Callback<News>() {
//请求成功时回调
@Override
public void onResponse(Call<News> call, Response<News> response) {
//请求处理,输出结果-response.body().show();
}
@Override
public void onFailure(Call<News> call, Throwable t) {
//请求失败时候的回调
}
});
上面是一个简单的GET请求的全过程。
补充一点,Retrofit还有个发起同步网络请求的方法:
//对发送请求进行封装
Call<News> news = mApi.getNews("1", "10");
//发送网络请求(同步)
Response<Reception> response = news.execute();