前言
从代理模式出发,逐步探索 Retrofit 的奥秘
代理模式
代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。
静态代理
关于静态代理本身的实现就很简单了,这里可以简单看一下实现。
java
public interface IFooDao {
void doSomething();
}
public class IFooDaoImpl implements IFooDao {
@Override
public void doSomething() {
System.out.println(this.getClass().getName() + " work");
}
}
public class IFooDaoProxy implements IFooDao {
private IFooDao mIFooDao;
public IFooDaoProxy(IFooDao mIFooDao) {
this.mIFooDao = mIFooDao;
}
@Override
public void doSomething() {
this.mIFooDao.doSomething();
}
}
依次创建了服务接口、服务实现、服务代理
java
public class Client {
public static void main(String[] args) {
IFooDao prox = new IFooDaoProxy(new IFooDaoImpl());
prox.doSomething();
}
}
静态代理本身的实现很简单,其中涉及到的类也会比较明确。这里需要注意的是,服务接口的实现不一定通过定义 接口类的方式实现(即不一定是 interface ),抽象类也是一种选择,比如 Android 中的 Activity 为了实现在不通过版本的兼容性,就是采用了 AppCompatDelegate
这个抽象类。Activity 本身并不负责实现 setContentView() 之类的功能。而是将其代理给 AppCompatDelegate
的实现类 AppCompatDelegateImpl
动态代理
可以看到,在静态代理中,代理类需要提前创建,而通过动态代理,就可以在运行期实现创建代理类的效果。
依次定义接口和注解
java
public interface IFooDao {
void doSomething();
@FakeProxy("proxy is yyds")
int add(@EasyProxy String a, @Nullable String b);
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface EasyProxy {
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FakeProxy {
String value() default "";
}
上面定义了接口和 EasyProxy
、FakeProxy
两个注解。
java
private static void dynamicProxy(IFooDao realDao) {
IFooDao proxy = (IFooDao) Proxy.newProxyInstance(
realDao.getClass().getClassLoader(),
realDao.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.printf("proxy=%s,method=%s,args=%s\n",
proxy.getClass().getSimpleName(), method.getName(), Arrays.toString(args));
System.out.println(method.getGenericReturnType());
System.out.println(Arrays.toString(method.getAnnotations()));
System.out.println(Arrays.toString(method.getGenericParameterTypes()));
System.out.println(Arrays.deepToString(method.getParameterAnnotations()));
// any-operation-before invoke
Object object = method.invoke(realDao, args);
// any-operation-after invoke
System.out.println("========================");
return object;
}
}
);
proxy.doSomething();
proxy.add("1", "2");
}
public class IFooDaoImpl implements IFooDao {
@Override
public void doSomething() {
System.out.println(this.getClass().getName() + " work");
}
@Override
public int add(@EasyProxy String a, @Nullable String b) {
return Integer.parseInt(a) + Integer.parseInt(b);
}
}
// main
dynamicProxy(new IFooDaoImpl());
可以看到,利用 Java 系统提供的 Proxy 静态方法 newProxyInstance
,只需要提供 IFooDao 的一个具体实现,那么就可以返回一个代理类,通过这个代理类 proxy 就可以访问 IFooDao 中定义的接口了。
可以看一下输出
shell
proxy=$Proxy0,method=doSomething,args=null
void
[]
[]
[]
com.dp.internal.proxy.IFooDaoImpl work
========================
proxy=$Proxy0,method=add,args=[1, 2]
int
[@com.dp.internal.proxy.FakeProxy(value=proxy is yyds)]
[class java.lang.String, class java.lang.String]
[[@com.dp.internal.proxy.EasyProxy()], []]
========================
可以看到在 InvocationHandler
的 invoke
方法中,我们通过 method 可以获取到关于当前 method 的所有信息,包括其参数、方法注解、参数注解等信息。这里值得注意的是 invoke
方法是有返回值的。
Retrofit
关于动态代理,Android 中使用最有名的莫过于 Retrofit 框架的实现了。下面就来看看 Retrofit 是如何使用动态代理的。
以下分析基于 com.squareup.retrofit2:retrofit:2.9.0
Retrofit 到底做了什么?
kotlin
interface ApiService {
@GET("wxarticle/chapters/json")
fun getAccountList(): Call<WeChatCountList>
@GET("wxarticle/chapters/json")
fun getAccountJson(): Call<ResponseBody>
@GET("wxarticle/chapters/json")
fun getWeChatAccountList(): Observable<WeChatCountList>
}
fun go2() {
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.build()
val service = retrofit.create(ApiService::class.java)
val call = service.getAccountJson()
}
这里需要注意的是,ApiService
中定义的前两个方法返回值都是 Call 类型的。getWeChatAccountList
返回的是 Observable 类型。但这对于 Retrofit 整体的实现来说是没有什么大的区别的,无非只是多了一个 CallAdapter
,需要把 Call 执行的结果用 Observeable
进行一次包装。使用 Retrofit 的本质还是为了发起网络请求,对于网络请求来说最重要的是通过 OkHttp 创建 Call 的实现。因此,从这个视角出发再去看 Retrofit 的实现就会发现他其实简单。
Retrofit build
java
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
// 在 Android 上,这个 Executor 就是主线程
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
// 除了在 build 过程中添加的自定义 CallAdapter,还要添加系统默认的 Adapter 。
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories =
new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters that consume all types.
// 内置的 Converter 主要是对返回的 Response 进行再次处理
converterFactories.add(new BuiltInConverters());
// build 阶段自定义添加的 Converter
converterFactories.addAll(this.converterFactories);
// 如果支持 Java,会添加支持 Optional 操作的 Converter
converterFactories.addAll(platform.defaultConverterFactories());
return new Retrofit(
callFactory,
baseUrl,
unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories),
callbackExecutor,
validateEagerly);
}
Retrofit 最核心的两个接口就是 CallAdapter
和 Converter
CallAdapter
java
/**
* Adapts a {@link Call} with response type {@code R} into the type of {@code T}. Instances are
* created by {@linkplain Factory a factory} which is {@linkplain
* Retrofit.Builder#addCallAdapterFactory(Factory) installed} into the {@link Retrofit} instance.
*/
public interface CallAdapter<R, T> {
/**
* Returns the value type that this adapter uses when converting the HTTP response body to a Java
* object. For example, the response type for {@code Call<Repo>} is {@code Repo}. This type is
* used to prepare the {@code call} passed to {@code #adapt}.
*
* <p>Note: This is typically not the same type as the {@code returnType} provided to this call
* adapter's factory.
*/
Type responseType();
/**
* Returns an instance of {@code T} which delegates to {@code call}.
*
* <p>For example, given an instance for a hypothetical utility, {@code Async}, this instance
* would return a new {@code Async<R>} which invoked {@code call} when run.
*
* <pre><code>
* @Override
* public <R> Async<R> adapt(final Call<R> call) {
* return Async.create(new Callable<Response<R>>() {
* @Override
* public Response<R> call() throws Exception {
* return call.execute();
* }
* });
* }
* </code></pre>
*/
T adapt(Call<R> call);
/**
* Creates {@link CallAdapter} instances based on the return type of {@linkplain
* Retrofit#create(Class) the service interface} methods.
*/
abstract class Factory {
/**
* Returns a call adapter for interface methods that return {@code returnType}, or null if it
* cannot be handled by this factory.
*/
public abstract @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit);
/**
* Extract the upper bound of the generic parameter at {@code index} from {@code type}. For
* example, index 1 of {@code Map<String, ? extends Runnable>} returns {@code Runnable}.
*/
protected static Type getParameterUpperBound(int index, ParameterizedType type) {
return Utils.getParameterUpperBound(index, type);
}
/**
* Extract the raw class type from {@code type}. For example, the type representing {@code
* List<? extends Runnable>} returns {@code List.class}.
*/
protected static Class<?> getRawType(Type type) {
return Utils.getRawType(type);
}
}
}
Converter
java
/**
* Convert objects to and from their representation in HTTP. Instances are created by {@linkplain
* Factory a factory} which is {@linkplain Retrofit.Builder#addConverterFactory(Factory) installed}
* into the {@link Retrofit} instance.
*/
public interface Converter<F, T> {
@Nullable
T convert(F value) throws IOException;
/** Creates {@link Converter} instances based on a type and target usage. */
abstract class Factory {
/**
* Returns a {@link Converter} for converting an HTTP response body to {@code type}, or null if
* {@code type} cannot be handled by this factory. This is used to create converters for
* response types such as {@code SimpleResponse} from a {@code Call<SimpleResponse>}
* declaration.
*/
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
Type type, Annotation[] annotations, Retrofit retrofit) {
return null;
}
/**
* Returns a {@link Converter} for converting {@code type} to an HTTP request body, or null if
* {@code type} cannot be handled by this factory. This is used to create converters for types
* specified by {@link Body @Body}, {@link Part @Part}, and {@link PartMap @PartMap} values.
*/
public @Nullable Converter<?, RequestBody> requestBodyConverter(
Type type,
Annotation[] parameterAnnotations,
Annotation[] methodAnnotations,
Retrofit retrofit) {
return null;
}
/**
* Returns a {@link Converter} for converting {@code type} to a {@link String}, or null if
* {@code type} cannot be handled by this factory. This is used to create converters for types
* specified by {@link Field @Field}, {@link FieldMap @FieldMap} values, {@link Header @Header},
* {@link HeaderMap @HeaderMap}, {@link Path @Path}, {@link Query @Query}, and {@link
* QueryMap @QueryMap} values.
*/
public @Nullable Converter<?, String> stringConverter(
Type type, Annotation[] annotations, Retrofit retrofit) {
return null;
}
/**
* Extract the upper bound of the generic parameter at {@code index} from {@code type}. For
* example, index 1 of {@code Map<String, ? extends Runnable>} returns {@code Runnable}.
*/
protected static Type getParameterUpperBound(int index, ParameterizedType type) {
return Utils.getParameterUpperBound(index, type);
}
/**
* Extract the raw class type from {@code type}. For example, the type representing {@code
* List<? extends Runnable>} returns {@code List.class}.
*/
protected static Class<?> getRawType(Type type) {
return Utils.getRawType(type);
}
}
}
- CallAdapter 顾名思义,就是把 OkHttp 的 Call 通过适配器转换成任意我们在接口方法中声明的返回类型,比如
Observeable
.如果没有添加 自定义的 CallAdapter ,Retrofit 默认返回的就是 Call 类型的 Call. 那么其中的 T 又是怎么决定的呢?这就要靠 Converter 了。OkHttp 默认返回的是Call<ResponseBody>
。 - Converter 就是对返回的 ResponseBody 进行类型转换,比如我们非常熟悉的
addConverterFactory(GsonConverterFactory.create())
.这个转换器具体是用什么库实现 json 到 object 的转换,对于 Retrofit 来说并不重要。
对于 Retrofit 来说,上述两个转换器内部的实现完全是透明的,Retrofit 真是按责任链模式,将数据一层层的传递给 callAdapterFactories
和 converterFactories
中剩余的转换器,直到全部消费完成。
那么这个过程具体是怎么展开的呢?下面就从 Retrofit 的创建展开分析。
Retrofit create 到底创建了啥?
java
public <T> T create(final Class<T> service) {
validateServiceInterface(service);
return (T)
Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] {service},
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
args = args != null ? args : emptyArgs;
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}
create 方法总结起来很简单,就是创建了传入的 service 这个类的一个动态代理。这里就是有意思的地方了,我们知道在 Java 中 interface 类型的类是无法创建实例的,比如 ApiService
这个接口,我们可以通过创建匿名类的方式实现这个接口,但是那样需要在实现接口的地方实现其所有方法的及其细节,这显然不是我们想要的。
而 Retrofit 通过动态代理,相当于是动态持有了接口实现的引用,而当我们单独调用接口中的每一个方法时,便会触发其 invoke 方法,在这个方法中通过 method 参数可以获取到当前调用方法的所有信息,通过这些信息就可以实现方法的具体细节了。可以说 Retrofit 的设计非常巧妙,利用接口完全解耦了定义和实现。
Retrofit 实现细节
kotlin
fun go2() {
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.build()
val service = retrofit.create(ApiService::class.java)
println("service is $service")
println("service is ${service.javaClass.name}")
val call = service.getAccountJson()
}
我们通过 create 创建了 ApiService 的实例。那么 service 到底是什么呢?我们可以看一下
shell
service is retrofit2.Retrofit$1@14d3bc22
service is com.sun.proxy.$Proxy0
可以看到这个实例的 Class 类型是 com.sun.proxy.$Proxy0 。这个类型其实就是 InvocationHandler 中 invoke 方法的第一个参数 Object 的类型。
再看调用链,当我们调用 service.getAccountJson() 方法时
可以看到通过动态代理会调用到 invoke 方法中,最终会调用 loadServiceMethod(method).invoke(args) 方法。
Retrofit 只有默认 CallAdapter 和 Converter 时的执行流程。
可以说 Retrofit 的整体设计非常的巧妙。