Retrofit 核心源码分析(一)- 注解解析和动态代理

Retrofit是目前Android平台上比较流行的网络请求框架之一,它提供了一种简洁、灵活的方式来处理HTTP请求和响应。Retrofit的设计目的是使网络请求的代码更加容易编写和阅读,同时还提供了许多有用的特性,如注解解析、动态代理等。在本文中,我们将对Retrofit的注解解析和动态代理进行详细的分析。

注解解析

在使用Retrofit时,我们通常会定义一个接口,该接口用于描述我们要请求的API接口。在这个接口中,我们可以使用注解来描述API的各个方面,如HTTP方法、请求URL、请求参数等。Retrofit会根据这些注解来生成相应的网络请求代码。下面是一个示例:

1
2
3
4
5
interface GitHubService {
@GET("users/{user}/repos")
fun listRepos(@Path("user") user: String): Call<List<Repo>>
}

在这个示例中,@GET注解表示这是一个HTTP GET请求,”users/{user}/repos”表示请求的URL,@Path(“user”)表示请求URL中的参数。Retrofit会解析这些注解,并生成相应的网络请求代码。

Retrofit中的注解解析是通过Retrofit.Builder中的retrofit2.Retrofit#create方法实现的。这个方法会返回一个代理对象,该代理对象会在调用接口方法时解析注解并生成相应的网络请求。

下面是retrofit2.Retrofit#create方法的核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}

该方法首先会验证接口是否满足要求,然后会返回一个代理对象。这个代理对象实现了接口中的所有方法,并在调用方法时解析注解并生成相应的网络请求。

我们可以看到,代理对象的实现是通过java.lang.reflect.Proxy类实现的。Proxy.newProxyInstance方法会返回一个代理对象,该代理对象实现了指定接口中的所有方法。当我们调用代理对象的方法时,代理对象会调用InvocationHandler.invoke方法,该方法中实现了注解解析和网络请求的生成。

在InvocationHandler.invoke方法中,首先会判断是否调用了Object类的方法,如果是,则直接返回该方法的执行结果。如果不是,则进一步判断是否调用了接口的默认方法,如果是,则使用Platform类调用默认方法。否则,就调用loadServiceMethod方法来解析注解并生成网络请求。

loadServiceMethod方法会首先从缓存中获取ServiceMethod对象,如果缓存中没有,则创建一个新的ServiceMethod对象。ServiceMethod对象包含了网络请求的相关信息,如HTTP方法、请求URL、请求参数等。ServiceMethod对象的创建是通过ServiceMethod.Builder类实现的,该类会解析接口方法上的注解并生成相应的网络请求。

下面是ServiceMethod.Builder类的核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw methodError("'"
+ Utils.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
responseConverter = createResponseConverter();
RequestFactory requestFactory = createRequestFactory();
return new ServiceMethod<>(requestFactory, callAdapter, responseConverter);
}

在ServiceMethod.Builder类中,首先会创建一个CallAdapter对象,该对象用于处理网络请求的结果。然后会检查responseType是否是Response或okhttp3.Response类型,如果是,则抛出异常。接下来,会创建一个ResponseConverter对象,该对象用于将网络请求的结果转换成Java对象。最后,会创建一个RequestFactory对象,该对象用于创建okhttp3.Request对象。

ServiceMethod对象包含了网络请求的相关信息,包括RequestFactory对象、CallAdapter对象和ResponseConverter对象。OkHttpCall对象则负责执行网络请求,并将结果传递给CallAdapter对象进行处理。CallAdapter对象最终将结果转换成Java对象并返回给调用者。

动态代理

在前面的代码中,我们已经看到了动态代理的使用。在Retrofit中,我们使用动态代理来实现注解解析和网络请求的生成。动态代理是一种机制,通过它我们可以在运行时创建一个代理对象,该代理对象会代替原始对象来执行方法调用。

在Retrofit中,我们使用动态代理来创建一个实现接口的代理对象。当我们调用代理对象的方法时,代理对象会调用InvocationHandler.invoke方法,该方法中实现了注解解析和网络请求的生成。因此,我们可以将网络请求的代码封装在接口中,使得我们的代码更加简洁和易于阅读。

下面是一个使用动态代理的简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.lang.reflect.*

interface HelloWorld {
fun sayHello()
}

class HelloWorldImpl : HelloWorld {
override fun sayHello() {
println("Hello, world!")
}
}

fun main() {
val proxy = Proxy.newProxyInstance(
DynamicProxyExample::class.java.classLoader,
arrayOf(HelloWorld::class.java),
object : InvocationHandler {
private val target: HelloWorld = HelloWorldImpl()
override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
println("Before method execution...")
val result = method?.invoke(target, *(args ?: emptyArray()))
println("After method execution...")
return result
}
}
) as HelloWorld
proxy.sayHello()
}

在这个示例中,我们定义了一个HelloWorld接口和一个HelloWorldImpl实现类。然后,我们使用动态代理创建了一个代理对象,该代理对象实现了HelloWorld接口。在InvocationHandlerinvoke方法中,我们首先输出一行日志,然后调用HelloWorldImpl对象的sayHello方法,最后再输出一行日志。当我们调用代理对象的sayHello方法时,代理对象会调用InvocationHandler.invoke方法,从而实现了在方法执行前后输出日志的功能。动态代理是一种非常强大的机制,可以用于实现很多功能,如性能分析、日志记录、事务管理等。在Retrofit中,我们使用动态代理来实现注解解析和网络请求的生成,从而使得我们的代码更加简洁和易于阅读。


Retrofit 核心源码分析(一)- 注解解析和动态代理
https://smallmarker.github.io/2023/03/07/Retrofit 核心源码分析(一)-注解解析和动态代理/
作者
smallmarker
发布于
2023年3月7日
许可协议