Retrofit简要分析
retrofit作为Android客户端高频使用库,我们很有必要深入了解它的运行机制。我觉得我们在刚开始使用retrofit的时候都被它那快速简洁的用法所折服,只用写一个接口,写几个抽象方法,添加上注解,就ok了好像代码是自动生成的一样。刚开始的我怀疑retrofit可能使用的是编译时注解来帮我们自动实现方法实体。
为什么我会这样写?
- 我们在编写自己的业务接口和方法的时候只用定义返回值类型和使用各种注解填充请求就可以了。
- 我们在使用的时候直接可以创建出接口的实例对象并调用其中的方法,接口的class是无法实例化相应对象的。
结果浅读了它的代码以后发现确实存在较大误差 尴尬,,ԾㅂԾ,,
它主要采取的方式为 动态代理 + 运行时注解
动态代理
首先我们来了解它是如何创建接口示例的,它采用了动态代理的方式,提到代理我们肯定会想到代理模式,但是我们一般采用的都是静态代理。
interface A {
void show();
}
class B implements A {
@Override
public void show() {
System.out.println("我烙一个饼");
}
}
class C implements A {
private final A a = new B(); // 此时依赖于抽象即可
@Override
public void show() {
a.show();
System.out.println("往饼上加点儿番茄酱"); // 增强方法
}
}
每当我们想要为一个类做增强就要新建一个类并编写代码。
我们使用动态代理以后是怎么做的?
interface B{
fun show()
}
class A{
override fun showLLL() {
println("你好 基础代码")
}
}
fun main() {
val a =
Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), arrayOf(B::class.java), object : InvocationHandler {
override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
// 增强代码 hear
return A().showLLL();
}
})
(a as? A)?.show()
}
//输出:你好 基础代码
我们创建了一个接口A的示例并且调用了它的show()方法,但是我们并没有具体去实现show()方法甚至于我们都没有对A有一个具体类实现这个接口。这个Proxy创建的示例a到底是什么?show()方法具体代码逻辑是由A类的showLLL方法做的,相对于静态代理增强代码甚至于可以不同方法名字,增强的类也可以不用实现接口,给了程序很大的灵活性。
为什么他是怎么做到的?这个a的类是怎么定义的?
我们可以使用
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(
@Path("owner") String owner,
@Path("repo") String repo);
}
byte[] hellos = ProxyGenerator.generateProxyClass("proxyClass", new Class[]{SimpleService.GitHub.class}, Modifier.FINAL | Modifier.PUBLIC);
FilesKt.writeBytes(new File("proxyClass.class"), hellos);
使用ProxyGenerator创建clas并将其输出到文件中,IDEA打开clas文件时自动反编译class为java代码。
public final class proxyClass extends Proxy implements GitHub {
private static Method m1;
private static Method m2; // toString()方法
private static Method m3; // 我们根据具体方法实现可以知道 m3:接口中的contributors()方法
private static Method m0;
public final Call contributors(String var1, String var2) throws {
try {
return (Call)super.h.invoke(this, m3, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
我们可以看到动态代理创建接口的示例并且继承自proxy,proxy中定义InvocationHandler,当我们不管调用哪一个方法都会调用InvocationHandler的invoke()方法,并且把调用时的参数,被调用方法的引用传给invocationHandler的invocation方法。当然Proxy跟ProxyGenerator不是同一个库中的我们只是为了解释大概动态代理做了什么?
做了什么?
动态的装载class不用手写,我们一般采用的方式:java -> class
这也就是动态代理可以实例化接口的秒处
方法最后都调用invoke()方法我们可以在invoke方法里边根据method参数转而执行跟method对应的具体方法。
在retrofit里边他也确实是这么做的。
我们看一下invoke()方法。
// retrofit.create()
// method缓存 ConcurrentHashMap网络请求可能会被调用在不同的线程,使用ConcurrentHashMap确保线程安全。
// 第一直觉就应该考虑到他是ConcurrentHashMap 不是HashMap 这叫业务敏感度 ————————bennohuo
private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
(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 {
// 逻辑简化版 invoke()方法
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
loadServiceMethod主要去缓存中查找相应的具体实现method
ServiceMethod:主要存储一些responseConverter,requestFactory,callFactory请求以及响应的时候直接调用相应的Converter就可以了避免遍历所有Converter查找合适的Converter。