前言
网上有很多流水线式的分析 Retrofit 原理,个人感觉比较枯燥乏味
所以这次通过二次封装 Intent 实现一个简单路由来学习 Retrofit
阅读这篇你能获得什么
- 通过实践进一步的了解 Retrofit 原理
- 运用设计模式封装,重复且无技术含量的代码
正文
一、基本使用流程
1、定义意图 API,描述意图跳转(注意:接口上面的注解表示是否检查登录状态如:未登录跳转至登录页面,方法上面的注解表示意图的目标页面,返回类型默认为 IntentCall,方法的参数即是意图的参数)
@CheckLogin
public interface CheckLoginStartActivityApi {
@TargetPage(PayActivity.class)
IntentCall startPayActivity(@Extra("key") String key);
}
2、创建 ERouter 并生成 API 的实现
// ERouter构建过程
ERouter router = new ERouter.Builder()
.setLoginActivityClass(LoginActivity.class)
.setLoginLogic(() -> LoginStatus.getLoginStatus())
.addRouterAdapterFactory(new MyRouterAdapterFactory())
.build();
// 动态代理的方式创建接口实现类过程
CheckLoginStartActivityApi startActivity = router.create(CheckLoginStartActivityApi.class);
3、调用 API 方法,生成 IntentCall,执行
IntentCall intentCall = startActivity.startPayActivity("name");
// 获取 Intent
intentCall.getIntent();
// 启动 Intent
intentCall.startIntent();
//PayActivity.Class
textView.setText(getIntent().getStringExtra("key"));
二、ERouter 动态代理创建接口实现类过程
1、create 方法
public <T> T create(final Class<T> service) {
// 解析接口上面的注解
parseTypeAnnotations(service);
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 这里 method 就是通过动态代理对象经过反射拿到 CheckLoginStartActivityApi.startPayActivity 方法对象
// 调用 loadStartActivityMethod 解析 method 对象方法上的注解和参数注解
StartActivityMethod startActivityMethod = loadStartActivityMethod(method);
return startActivityMethod.invoke(getTypeParameter(proxy), args);
}
});
}
2、Proxy.newProxyInstance 方法
Proxy.newProxyInstance 源码其实比较有意思
在 Android SDK 中是通过调用 native 方法,动态生成代理类性能会比较高效
在 Java 8 源码中使用的是 ProxyGenerator.generateProxyClass 生成代理类
//Java 8 源码
//Proxy 的内部类 ProxyClassFactory
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
...
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// 重点,生成一个代理类
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
可以发现在 ProxyClassFactory 类中的 apply 方法调用了 ProxyGenerator.generateProxyClass 返回了一个 byte[] proxyClassFile ,这个变量存的就是代理类字节码,直接祭出输出流
// 代理类的庐山真面目
// 稍微改动了一下,便于阅读
public final class CheckLoginStartActivityApiImpl extends Proxy implements CheckLoginStartActivityApi {
// 反射获取的方法
private static Method startPayActivity;
public CheckLoginStartActivityApiImpl(InvocationHandler invocationHandler) {
super(invocationHandler);
}
public final IntentCall startPayActivity(String key) {
try {
// h = invocationHandler 就是之前 Proxy.newProxyInstance 传入的 new InvocationHandler()
// 外部调用startPayActivity 方法就会回调给 InvocationHandler 接口的 invoke 方法
// 有没有一种切面(AOP)的感觉?
return (IntentCall)super.h.invoke(this, startPayActivity, new Object[]{key});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
static {
try {
// 反射获取方法
startPayActivity = Class.forName("com.dongchao.sample.CheckLoginStartActivityApi").getMethod("startPayActivity", Class.forName("java.lang.String"));
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
3、代码执行时序图
// 动态代理的方式创建接口实现类过程
CheckLoginStartActivityApi startActivity = router.create(CheckLoginStartActivityApi.class);
IntentCall intentCall = startActivity.startPayActivity("name");
4、小结
router.create 方法中调用 Proxy.newProxyInstance 动态创建代理类 CheckLoginStartActivityApiImpl 加载到内存,接着获取它的 Class 反射调用它构造函数传入 InvocationHandler
startActivity.startPayActivity = CheckLoginStartActivityApiImpl.startPayActivity 执行 super.h.invoke 回调接口到 InvocationHandler.invoke 然后执行 loadStartActivityMethod.invoke 返回值就是接口定义的返回类型
特别感谢能看到这里的大佬们,如果有什么问题欢迎评论指出
三、ERouter 注解解析
synchronized 的使用
程序中合理使用 synchronized 能有效提升程序执行效率
StartActivityMethod loadStartActivityMethod(Method method) {
// 获取缓存中已经解析过注解的方法,这也是 Retrofit 性能高效的原因
StartActivityMethod result = startActivityMethodCache.get(method);
// 避免非必要加锁,提示执行效率
if (result != null) {
return result;
}
// 这里加锁的意义,为了避免多线程下 StartActivityMethod.parseAnnotations 多次执行,影响性能(反射比较耗时)
synchronized (startActivityMethodCache) {
result = startActivityMethodCache.get(method);
if (result == null) {
result = StartActivityMethod.parseAnnotations(this, method);
startActivityMethodCache.put(method, result);
}
}
return result;
}
接着看 StartActivityMethod.parseAnnotations 内部
// StartActivityMethod.class
static StartActivityMethod parseAnnotations(ERouter router, Method method) {
// 真正去做解析的类
RequestParameterFactory requestBody = RequestParameterFactory.parseAnnotations(router, method);
return RouterStartActivityMethod.parseAnnotations(router, method, requestBody);
}
// RequestParameterFactory.class
static RequestParameterFactory parseAnnotations(ERouter router, Method method) {
return new Builder(router, method).build();
}
继续看 new Builder(router, method).build()
RequestParameterFactory build() {
//方法注解解析
for (Annotation annotation : methodAnnotations) {
if (annotation instanceof TargetPage) {
TargetPage targetPage = (TargetPage) annotation;
// 获取到方法上 TargetPage 注解的值
targetClass = targetPage.value();
checkLogin = targetPage.checkLogin();
}
}
//参数注解解析
for (int p = 0; p < parameterCount; p++) {
parameterKeys[p] = parseParameter(parameterAnnotationsArray[p]);
}
return new RequestParameterFactory(this);
}
//参数注解解析
String parseParameter(Annotation[] annotations) {
String result = null;
if (annotations != null) {
// 如果方法参数没有用注解那么 annotations 为空数组
for (Annotation annotation : annotations) {
String key = parseParameterAnnotation(annotation);
if (TextUtils.isEmpty(key)) {
continue;
}
if (!TextUtils.isEmpty(result)) {
throw new RuntimeException("当前参数有多个注解, 只允许存在一个");
}
result = key;
}
}
if (TextUtils.isEmpty(result)) {
throw new RuntimeException("参数无注解或注解内容为空");
}
return result;
}
String parseParameterAnnotation(Annotation annotation) {
if (annotation instanceof Extra) {
Extra extra = (Extra) annotation;
// 获取到参数 Extra 注解上的值
return extra.value();
}
return null;
}
解析接口方法上的注解和参数上的注解和 Retrofit 解析注解原理基本无差别
解析结束后返回 RequestParameterFactory 对象 接着看 RouterStartActivityMethod.parseAnnotations
// RouterStartActivityMethod.class
static RouterStartActivityMethod parseAnnotations(ERouter router, Method method,
RequestParameterFactory requestBody) {
// 通过返回值类型,遍历符合要求的适配器
RouterAdapter routerAdapter = router.routerAdapter(method.getReturnType());
// 创建 RouterStartActivityMethod 的子类,传入解析的注解数据和适配器
return new IntentAdapted(requestBody, routerAdapter);
}
public RouterAdapter<?> routerAdapter(Type returnType) {
for (int i = 0; i < routerAdapterFactories.size(); i++) {
RouterAdapter<?> adapter = routerAdapterFactories.get(i).get(returnType);
if (adapter != null) {
return adapter;
}
}
throw new IllegalArgumentException("没有合适的 routerAdapter");
}
最终返回 IntentAdapted 实例
小结
原理就是构建一个 StartActivityMethod(IntentAdapted)子类实例,有两个成员变量 RequestParameterFactory 和 RouterAdapter
前者保存着解析出的注解信息,后者负责创建接口方法返回值也就是 IntentCall 的实现类(ExecutorIntentCall)
四、ERouter 构建 Intent
StartActivityMethod.invoke 方法解析
// startActivityMethod = IntentAdapted 实例
startActivityMethod.invoke(getTypeParameter(proxy), args)
// RouterStartActivityMethod.class
// 执行 RouterStartActivityMethod.invoke 方法,RouterStartActivityMethod 是 IntentAdapted 的父类
@Override
final ReturnT invoke(TypeParameter typeParameter, Object[] args) {
// 里面通过建造者模式创建 JumpIntent
JumpIntent jumpIntent = requestBody.createJumpIntent(typeParameter, args);
return adapt(jumpIntent);
}
// RequestParameterFactory.class
JumpIntent createJumpIntent(TypeParameter typeParameter, Object[] args) {
boolean isCheckLogin;
if (typeParameter != null) {
isCheckLogin = typeParameter.isCheckLogin ? true : checkLogin;
} else {
isCheckLogin = checkLogin;
}
int argumentCount = args == null ? 0 : args.length;
if (parameterKeys.length != argumentCount) {
throw new IllegalArgumentException("注解和方法参数不匹配");
}
Map<String, Object> parameterMap = new ArrayMap(argumentCount);
// 注解 key 和 value 参数放入 ArrayMap
// 在 JumpIntent.putExtra 方法中实现 Intent 传递参数
for (int i = 0; i < argumentCount; i++) {
parameterMap.put(parameterKeys[i], args[i]);
}
return new JumpIntent.Builder()
.setLoginActivityClass(loginActivityClass)
.setTargetClass(targetClass)
.setCheckLogin(isCheckLogin)
.setLoginLogic(loginLogic)
.setParameterMap(parameterMap)
.build();
}
JumpIntent 是 IntentCall 的实现类
public class JumpIntent implements IntentCall {
...
public Intent getIntent() {
if (currentContext == null) {
return null;
}
if (intent == null) {
if (isCheckLogin) {
intent = getIntentLoginOrTarget();
} else {
intent = new Intent(currentContext, targetClass);
}
//设置参数
putExtra(intent);
}
return intent;
}
@Override
public boolean startIntent() {
Intent intent = getIntent();
if (intent == null) {
return false;
}
currentContext.startActivity(intent);
return true;
}
}
代码比较简单,就贴稍微重点一些的
接下来看 IntentAdapted.adapt 方法
static final class IntentAdapted<ReturnT> extends RouterStartActivityMethod<ReturnT> {
...
@Override
protected ReturnT adapt(IntentCall intentCall) {
// 把 JumpIntent 传入适配器
return (ReturnT) routerAdapter.adapt(intentCall);
}
}
public class DefaultRouterAdaptedFactory extends RouterAdapter.Factory {
@Override
public RouterAdapter get(Type returnType) {
if (getRawType(returnType) == IntentCall.class) {
return new RouterAdapter() {
// routerAdapter.adapt(intentCall) 其实就是走到了这里
@Override
public IntentCall adapt(IntentCall intentCall) {
// intentCall 就是 JumpIntent 对象
return new ExecutorIntentCall(intentCall);
}
};
}
return null;
}
// 静态代理对象
static final class ExecutorIntentCall implements IntentCall {
private static final String TAG = "ExecutorIntentCall";
//目标对象 IntentCall = JumpIntent
private final IntentCall delegate;
private static Handler mHandler;
public ExecutorIntentCall(IntentCall delegate) {
this.delegate = delegate;
this.mHandler = new Handler(Looper.getMainLooper());
}
@Override
public Intent getIntent() {
AppLog.i(TAG, "delegate getIntent start");
// 在调用目标对象方法前可以做一些业务如:埋点、快速点击的逻辑判断,防止创建多个 Activity 实例的问题
Intent intent = delegate.getIntent();
AppLog.i(TAG, "delegate getIntent end");
return intent;
}
@Override
public boolean startIntent() {
AppLog.i(TAG, "delegate startIntent start");
runInMainThread(() -> delegate.startIntent());
AppLog.i(TAG, "delegate startIntent end");
return true;
}
private void runInMainThread(Runnable runnable) {
if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
mHandler.post(runnable);
} else {
runnable.run();
}
}
}
routerAdapter.adapt(intentCall) 的 routerAdapter 就是 DefaultRouterAdaptedFactory.get 通过工程模式创建的 RouterAdapter 对象实例,然后调用它的 adapt 创建 ExecutorIntentCall 静态代理对象,持有着 intentCall (也就是 JumpIntent 实例) ,为了方便在调用目标对象方法前可以做一些业务如:埋点或快速点击的逻辑判断,防止创建多个 Activity 实例的问题等
放张图可能更加清晰点
Retrofit 默认的适配器,就是通过静态代理对象把请求结果回调到主线程
我这里通过 Handler 把启动 Activity 放到主线程中执行,算是小小的致敬
总结
到这里也算是结束了
Retrofit 是 Android 最热门的框架,熟悉其原理和流程还是非常有必要的,之前也看过源码,但是没有深入了解他的实现原理。经过实践后发现,不得不佩服 Retrofit 作者为了保证易用性和扩展性而引入大量的思考所设计的框架。