Retrofit 是一个类型安全的 HTTP 客户端库,用于在 Java 和 Android 中简化网络请求和服务器的交互。它通过注解来配置网络请求参数、处理响应数据,并且支持多种数据解析方式。以下是 Retrofit 的详细介绍和运用。
Retrofit 的核心组件
- Retrofit 实例:创建 Retrofit 实例是使用 Retrofit 的第一步。通过 Retrofit.Builder 类来配置和创建 Retrofit 实例。
- 接口方法:定义一个接口,使用注解来描述 HTTP 请求。
- CallAdapter 和 Converter:CallAdapter 负责将 Call 对象转换成其他类型的对象,Converter 负责将 HTTP 响应体转换成 Java 对象。
Retrofit 的使用步骤
1. 添加依赖
首先,在项目的 build.gradle
文件中添加 Retrofit 和 Gson Converter 的依赖。
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.x.x'
implementation 'com.squareup.retrofit2:converter-gson:2.x.x'
}
2. 创建 Retrofit 实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/") // 设置基础 URL
.addConverterFactory(GsonConverterFactory.create()) // 添加 Gson 转换器
.build();
3. 定义接口
使用注解来定义 HTTP 请求的接口。
public interface ApiService {
@GET("users/{id}")
Call<User> getUser(@Path("id") int userId);
@POST("users")
Call<User> createUser(@Body User user);
// 更多请求方法...
}
4. 创建接口实例并调用
ApiService apiService = retrofit.create(ApiService.class);
// 发起 GET 请求
Call<User> call = apiService.getUser(1);
call.enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
if (response.isSuccessful()) {
User user = response.body();
// 处理响应数据
}
}
@Override
public void onFailure(Call<User> call, Throwable t) {
// 处理错误
}
});
// 发起 POST 请求
Call<User> callPost = apiService.createUser(new User("username", "email"));
callPost.enqueue(new Callback<User>() {
// onResponse 和 onFailure 的实现同上
});
Retrofit 的注解
Retrofit 提供了多种注解来配置网络请求:
- @GET、@POST、@PUT、@DELETE、@HEAD、@OPTIONS、@PATCH:指定请求方法。
- @HTTP:自定义请求方法。
- @Headers:添加固定请求头。
- @Header:添加动态请求头。
- @Url:替换基础 URL。
- @Path:替换 URL 中的参数。
- @Query、@QueryMap:添加查询参数。
- @Body:发送请求体。
- @FormUrlEncoded、@Field、@FieldMap:发送表单数据。
异步和同步调用
Retrofit 支持异步和同步调用:
- 异步调用:使用
enqueue
方法,并在Callback
中处理响应。 - 同步调用:使用
execute
方法,它将阻塞调用线程直到响应返回。
Retrofit 的优势
- 简洁的 API:通过注解来简化代码。
- 类型安全:利用 Java 的强类型系统来减少错误。
- 易于测试:可以轻松地模拟网络请求和响应。
- 可定制性:支持多种数据解析方式,如 Gson、Jackson、Moshi 等。
Retrofit 的内部工作机制涉及多个关键组件和步骤,下面将详细介绍这些组件和步骤的工作原理。
动态代理和反射
我们先了解下什么是动态代理,什么是静态代理
静态代理:
- 定义:静态代理在编译时就已经确定了被代理的类。它通常需要手动编写代理类,这个代理类会实现与被代理类相同的接口。
- 使用方式:在静态代理中,代理类会在其方法中调用被代理对象的方法,并在调用前后添加额外的处理逻辑。例如,可以在调用前进行权限验证,或在调用后进行结果处理。
- 缺点:静态代理的实现比较直接,但需要为每个被代理类的方法编写相应的代理方法,这可能会导致代码冗余和维护困难。
动态代理:
- 定义:与静态代理不同,动态代理是在运行时才确定被代理的类。它不需要手动编写代理类,而是通过Java的反射机制动态生成代理类。
- 核心原理:动态代理主要涉及到
InvocationHandler
接口和Proxy
类。InvocationHandler
接口中的invoke
方法用于处理所有对代理对象的调用,而Proxy
类用于动态创建代理对象。 - 优点:动态代理提供了更大的灵活性,因为它可以在运行时动态地创建代理类,而不需要预先定义它们。这使得它特别适合于需要对大量方法进行代理的场景。
总的来说,静态代理和动态代理都用于间接访问目标对象,以减少系统复杂性或增加额外的功能。选择使用哪种代理模式取决于具体的应用需求和场景。
动态代理实现细节
public <T> T create(final Class<T> service) {
//.....省略...
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
//这里看来原方法不一定会被调用,主要执行代理的操作
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
//核心的三行代码
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method); //创建的代理方法对象,其内部包含了login方法的注解参数,返回值,以及各个工厂以及各请求器转换器等等的适配操作
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); //创建一个网络请求器,所以说Retrofit底层默认是使用okhttp实现的
return serviceMethod.adapt(okHttpCall); //将okhttpcall对象与网络请求适配器进行绑定
}
});
}
动态代理在 Retrofit 中用于创建接口的代理实现。以下是动态代理实现的关键步骤:
- 创建代理实例:Retrofit 使用
Proxy.newProxyInstance
方法创建一个实现了ApiService
接口的代理对象。 - 方法调用拦截:当代理实例的方法被调用时,代理会拦截这个调用,并将调用转发给
InvocationHandler
。 - 处理方法调用:
InvocationHandler
的invoke
方法被调用,Retrofit 在这里处理注解,创建ServiceMethod
对象,并最终执行网络请求。
一个ServiceMethod对象对应了一个网络接口中的方法,该对象中保存有方法的处理网络请求所要用到的各种参数及方法的属性注解等
反射解析方法细节
在 loadServiceMethod
方法中,首先会从缓存中获取ServiceMethod对象,为null才会创建实例对象,在Builer()中会将解析method一些信息(参数、注解、参数注解)和retrofit对象中的属性,并保存到serviceMethod对象中。
Retrofit 使用反射来解析方法上的注解和参数:
- 解析方法上的注解:Retrofit 解析接口方法的注解,如
@GET
、@POST
等,以确定请求的 URL、方法类型等。 - 解析方法参数上的注解:Retrofit 解析方法参数上的注解,如
@Path
、@Query
、@Body
等,以确定如何将参数值添加到请求中。
ServiceMethod
构建 ServiceMethod.Builder 细节
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
responseConverter = createResponseConverter();
ServiceMethod.Builder
类的 build
方法执行以下步骤:
1.用于从网络请求适配器工厂中获取一个网络请求适配器
2.调用上面的responseType()用以check返回值类型的定义
3. 用于获取一个响应数据转换器,若使用GsonConverterFactory,在调用接口得到返回值后会通过gson转换为对应类型的response对象,
执行请求细节
ServiceMethod
的 invoke
方法负责以下操作:
- 创建 OkHttpCall 实例:Retrofit 创建
OkHttpCall
实例,该实例负责实际的网络请求。实现Call接口,重写了execute()和enqueue方法等,在随后的网络请求适配器调用中,就会执行此方法来通过OkHttp请求访问网络。 - 返回适配器转换后的 Call 对象:Retrofit 使用
ServiceMethod
的adapt
方法将OkHttpCall
转换为用户期望的Call
类型,例如Observable
或CompletableFuture
。
OkHttpCall
发送请求细节
OkHttpCall
的 execute
和 enqueue
方法负责发送请求。以下是 execute
方法的简化实现:
@Override
public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
if (creationFailure != null) {
throw creationFailure;
}
call = rawCall;
if (call == null) {
try {
call = rawCall = createRawCall();
} catch (IOException | RuntimeException e) {
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
return parseResponse(call.execute());
}
在这个方法中,createRawCall
方法被调用来创建一个 okhttp3.Call
对象,该对象负责实际的网络请求。
Converter
这边看下parseResponse(call.execute())和parseResponse(rawResponse)
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
//访问成功后,会将响应body转换为用户要求的格式
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
try {
//最后回调上层的callback对象方法
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
//至于剩余部分,则是访问失败回调上层接口,比较简单,不在概述
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();//获取body响应体
int code = rawResponse.code();
if (code < 200 || code >= 300) {.....} //如果响应码不在200之内,则为请求失败回调failure()
if (code == 204 || code == 205) {
Response.success(null, rawResponse) //返回空响应体的成功回调
}//凡是200以内的都已经访问成功
T body = serviceMethod.toResponse(catchingBody);//关键在这一行
//responseConverter.convert(body)内部执行了这样一行代码,使用响应体转换工厂来调用convert()进行数据转换
响应转换细节
Converter 负责将 HTTP 响应体转换为 Java 对象,或将 Java 对象转换为 HTTP 请求体。以下是响应转换的关键步骤:
- 获取 Converter:Retrofit 根据 API 接口返回类型获取 Converter。
- 转换响应体:Retrofit 使用 Converter 将 OkHttp 的
ResponseBody
转换为接口方法返回的类型。
这里举一个GsonResponseBodyConverter(GsonConverterFactory创建)的例子
@Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
T result = adapter.read(jsonReader); //就是使用adapter将响应体的二进制流重新转换为对应的返回值类型,具体是什么类型,在serviceMethod中已经确定好并且对其赋过值了
if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
throw new JsonIOException("JSON document was not fully consumed.");
}
return result;
...
}
总结
Retrofit 的底层原理涉及多个关键组件和步骤,包括动态代理、反射、ServiceMethod 的创建和解析、OkHttp 的使用以及 Converter 的使用。通过这些组件和步骤,Retrofit 提供了一个高效、灵活且易于使用的网络请求框架。