什么是Retrofit2
Retrofit是一个给Android和Java用的类型安全的HTTP客户端,它将网络请求封装成接口,并采用注解的形式声明请求,由Retrofit自动生成接口实现对象给开发者调用。
Retofit2的入门
- 引用Retrofit2
Gradle引用
compile 'com.squareup.retrofit2:retrofit:2.1.0'
Maven引用
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.1.0</version>
</dependency>
- Retrofit2入门
接下来我们一步一步的学习使用Retrofit2的使用。
- 创建Retrofit2实例
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://wthrcdn.etouch.cn/").build();
创建Retrofit2实例时需要通过Retrofit.Builder方法,并调用baseUrl方法设置根URL。Retrofit2的baseUlr必须以/结束,不然会抛出一个IllegalArgumentException。
- 定义一个和HTTP的API对应的接口
/** * 天气服务 */
public interface WeatherService {
@GET("weather_mini")
Call<WeatherBean> getWeatherInfo(@Query("city") String city);
}
- 生成网络请求接口的对象
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://wthrcdn.etouch.cn/")
.addConverterFactory(GsonConverterFactory.create(gson))//这块定义了一个转换器,之后会讲解
.build();
WeatherService service = retrofit.create(WeatherService.class);
- 调用实现的方法来进行同步或异步的HTTP请求
Call<WeatherBean> call1 = service.getWeatherInfo("沈阳");
//异步请求
call1.enqueue(new Callback<WeatherBean>() {
@Override
public void onResponse(Call<WeatherBean> call, Response<WeatherBean> response) {
Log.d("Retrofit2Example", response.body().getDesc());
}
@Override
public void onFailure(Call<WeatherBean> call, Throwable t) {
Log.d("Retrofit2Example", t.getMessage());
}});
try {
//同步请求
Response<WeatherBean> response = call1.execute();
if (response.isSuccessful()) {
Log.d("Retrofit2Example", response.body().getDesc());
}
} catch (IOException e) {
e.printStackTrace();
}
以上就是一个非常简单的GET请求,可以看到通过Retrofit2的create()
方法生成网络接口的实现对象,之后调用具体的接口调用方法会生成一个请求对应的Call
对象,在这个时候请求并没有发送出去,接下来调用Call
对象的enqueue
或者execute
方法实现异步和同步的网络请求。
- 详细分析每一步
- Retrofit2对象的创建
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://wthrcdn.etouch.cn/").build();
可以看到我们可以使用Retrofit.Builder().build()
这个方法创建一个Retrofit2对象,在创建的时候还可以使用baseUrl()
方法为Retrofit2对象设置一个根路径。这块很好理解,那么我们再介绍一下Retrofit2对象创建的时候还可以做的一些操作。
1.首先介绍一下转换器,转换器顾名思义就是将服务端返回的Response
进行转换。在默认情况下Retrofit只支持将HTTP的响应体转换换为ResponseBody
。比如要用Gson解析的话就需要添加依赖并使用addConverterFactory
方法设置转换器。
<!-- 引入gson -->
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd hh:mm:ss").create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://wthrcdn.etouch.cn/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
上面的代码首先创建了一个gson对象,之后再创建retrofit对象的时候使用addConverterFactory方法设置了转换器。经过这个设置之后呢我们在之后的请求中就可以使用泛型去作为请求体或者返回值了。
/** * 天气服务 */
public interface WeatherService {
@GET("weather_mini")
Call<WeatherBean> getWeatherInfo(@Query("city") String city);
}
类似这块的代码,返回值就是一个DTO,我们看一下这个DTO内部代码。
public class WeatherBean {
private String desc;
private int status;
private Data data;
public void setDesc(String desc) {
this.desc = desc;
}
public String getDesc() {
return this.desc;
}
public void setStatus(int status) {
this.status = status;
}
public int getStatus() {
return this.status;
}
public void setData(Data data) {
this.data = data;
}
public Data getData() {
return this.data;
}
}
当我们设置了gson转换器之后服务端返回的json格式的字符串就会被转换成相应的对象了。当然如果你没有设置转换器的话这块的泛型就是默认的ResponseBody
了。
/** * 天气服务 */
public interface WeatherService {
@GET("weather_mini")
Call<ResponseBody> getWeatherInfo(@Query("city") String city);
}
- 自定义Converter
retrofit自带的转换器:- Gson: com.squareup.retrofit2:converter-gson
- Jackson:com.squareup.retrofit2:converter-jackson
- Moshi:com.squareup.retrofit2:converter-moshi
- Protobuf:com.squareup.retrofit2:converter-protobuf
- Wire:com.squareup.retrofit2:converter-wire
- Simple XML:com.squareup.retrofit2:converter-simplexml
- Scalars(primitives,boxed,String):com.squareup.retrofit2:converter-scalars
如果自带的转换器不能满足我们的需要,这个时候我们就需要自定义一个转换器Converter
了。
public interface Converter<F, T> {
// 实现从 F(rom) 到 T(o)的转换
T convert(F value) throws IOException;
// 用于向Retrofit提供相应Converter的工厂
abstract class Factory {
// 这里创建从ResponseBody向其它类型的Converter,如果不能转换返回null
// 主要用于对响应体的处理
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return null;
}
// 在这里创建 从自定类型到RequestBody的Converter,不能处理就返回null,
// 主要用于对Part、PartMap、Body注解的处理
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return null;
}
// 这里用于对Field、FieldMap、Header、Path、Query、QueryMap注解的处理
// Retrfofit对于上面的几个注解默认使用的是调用toString方法
public Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return null;
}
}
}
比如我们要定义Call<ResponseBody>
转换为Call<String>
的转换器,那么对应的F和T则分别对应ResponseBody
和String
,我们定义一个StringConverter
并实现Converter
接口。
public static class StringConverter implements Converter<ResponseBody, String> {
public static final StringConverter INSTANCE = new StringConverter();
@Override
public String convert(ResponseBody value)throws IOException {
return value.string();
}
}
还需要一个Factory来向Retrofit注册StringConverter。
public static class StringConverterFactory extends Converter.Factory {
public static final StringConverterFactory INSTANCE = new StringConverterFactory();
public static StringConverterFactory create() {
return INSTANCE;
}
// 我们只关实现从ResponseBody 到 String 的转换,所以其它方法可不覆盖
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if (type == String.class) {
return StringConverter.INSTANCE;
}
//其它类型我们不处理,返回null就行
return null;
}
}
好了,之后我们再使用Retrofit.Builder.addConverterFactory
向Retrofit注册我们StringConverterFactory
。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://wthrcdn.etouch.cn/") // 如是有Gson这类的Converter 一定要放在其它前面
.addConverterFactory(StringConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
这里addConverterFactory
是有先后顺序的,如果有多个ConverterFactory
都支持同一种类型,那么就是只有第一个才会被使用,而GsonConverterFactory
是不判断是否支持的,所以这里交换顺序还会有一个异常抛出,原因是类型不匹配。现在只要返回值类型的泛型参数是String
就会由我们的StringConverter处理。