WMRouter的核心原理大概就是,通过注解标注路由信息,在编译期动态扫描路由信息,生成加载路由表信息的java类。并利用 gradle transform和asm生成加载全部路由信息的class文件。在app运行时,路由框架反射调用这个class文件,从而完成了路由表的装载。
你必须知道的
annotationProcessor
https://blog.csdn.net/xx326664162/article/details/68490059
Java 注解(Annotation)
https://juejin.im/entry/5b286169e51d4558de5bcd80
JavaPoet
https://juejin.im/entry/58fefebf8d6d810058a610de
AOP 是一种面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
ASM 是一个框架可以看作 AOP 的工具
它可以直接以二进制形式用于修改现有类或动态生成类。
Gradle插件包含了一个叫Transform的API,这个API允许第三方插件在class文件转为为dex文件前操作编译好的class文件,这个API的目标是简化自定义类操作,而不必处理Task,并且在操作上提供更大的灵活性。并且可以更加灵活地进行操作。
Transform每次都是将一个输入进行处理,然后将处理结果输出,而输出的结果将会作为另一个Transform的输入
自定义Gradle插件
https://blog.csdn.net/huachao1001/article/details/51810328
通过自定义 Gradle 插件修改编译后的 class 文件
https://juejin.im/entry/577b03438ac2470061afb130
http://lyldalek.cn/2019/09/10/blog_bak/Blog/Android-%E9%AB%98%E7%BA%A7/Gradle%20Transform%20API%20%EF%BC%9A%E7%9B%B4%E6%8E%A5%E5%A4%84%E7%90%86%20class%20%E6%96%87%E4%BB%B6/
SPI
Service Provider Interface,下面是一段官方的解释,其实就是为某个接口寻找服务的机制,有点类似IOC的思想,将装配的控制权移交给ServiceLoader。
https://www.jianshu.com/p/deeb39ccdc53
RouterUri注解
最常用,基本只用这个注解就可以满足URI分发需求。根据URI的scheme+host,寻找并分发给对应的PathHandler,之后PathHandler再根据path匹配RouterUri注解配置的节点。
参数如下:
path:跳转URI要用的path,必填。path应该以"/"开头,支持配置多个path。
scheme、host:跳转URI的scheme和host,可选。
exported:是否允许外部跳转,可选,默认为false。
interceptors:要添加的Interceptor,可选,支持配置多个。
代码块
Java
public static final String ROUTER_PATH_DRIVER_COMMON_INFO = “/user/driverCommonInfo”;
@RouterUri(path = RouterConstants.ROUTER_PATH_DRIVER_COMMON_INFO)
public class DriverCommonInfoActivity extends BaseMvvmActivity
发起URI跳转
下面这种方式应该是较为常用的一种方式
代码块
Java
new DefaultUriRequest(activity,
RouterConstants.getRouterUri(RouterConstants.ROUTER_PATH_DRIVER_COMMON_INFO))
.putExtra(EXTRA_DRIVER_COMMON_INFO_FILL_FLAG, driverCommonInfoFillFlagFrom)
.start();
根据UriRequest的 scheme+host获取对应的PathHandler,交由PathHandler处理,而PathHandler其实就是根据Uri的path,获取对应的UriHandler来处理这个uri。
UriAnnotationHandler提供了register方法来向其中注册uri。它会根据uri来生成对应的UriHandler。
把WMRouter的代码down下来,可以看到
interfaces 声明了注解接口
complier 主要继承AbstractProcessor添加了注解的处理
plugin 主要是生成包含全部路由信息的class文件
对RouterUri和UriAnnotationProcessor进行分析
代码块
Java
package com.sankuai.waimai.router.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- 指定一个Uri跳转,此注解可以用在Activity和UriHandler上
- Created by jzj on 2018/3/19.
/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface RouterUri {
/*- path
/
String[] path();
/* - scheme
/
String scheme() default “”;
/* - host
/
String host() default “”;
/* - 是否允许外部跳转
/
boolean exported() default false;
/* - 要添加的interceptors
*/
Class[] interceptors() default {};
}
代码块
Java
package com.sankuai.waimai.router.compiler;
import com.google.auto.service.AutoService;
import com.sankuai.waimai.router.annotation.RouterUri;
import com.sankuai.waimai.router.interfaces.Const;
import com.squareup.javapoet.CodeBlock;
import com.sun.tools.javac.code.Symbol;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.MirroredTypesException;
import javax.lang.model.type.TypeMirror;
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class UriAnnotationProcessor extends BaseProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
if (annotations == null || annotations.isEmpty()) {
return false;
}
CodeBlock.Builder builder = CodeBlock.builder();
String hash = null;
for (Element element : env.getElementsAnnotatedWith(RouterUri.class)) {
if (!(element instanceof Symbol.ClassSymbol)) {
continue;
}
boolean isActivity = isActivity(element);
boolean isHandler = isHandler(element);
if (!isActivity && !isHandler) {
continue;
}
Symbol.ClassSymbol cls = (Symbol.ClassSymbol) element;
RouterUri uri = cls.getAnnotation(RouterUri.class);
if (uri == null) {
continue;
}
if (hash == null) {
hash = hash(cls.className());
}
CodeBlock handler = buildHandler(isActivity, cls);
CodeBlock interceptors = buildInterceptors(getInterceptors(uri));
// scheme, host, path, handler, exported, interceptors
String[] pathList = uri.path();
for (String path : pathList) {
builder.addStatement(“handler.register($S, $S, $S, $L, L)”,
uri.scheme(),
uri.host(),
path,
handler,
uri.exported(),
interceptors);
}
}
if (hash == null) {
hash = randomHash();
}
buildHandlerInitClass(builder.build(), “UriAnnotationInit” + Const.SPLITTER + hash,
Const.URI_ANNOTATION_HANDLER_CLASS, Const.URI_ANNOTATION_INIT_CLASS);
return true;
}
private static List<? extends TypeMirror> getInterceptors(RouterUri scheme) {
try {
scheme.interceptors();
} catch (MirroredTypesException mte) {
return mte.getTypeMirrors();
}
return null;
}
@Override
public Set getSupportedAnnotationTypes() {
return new HashSet<>(Collections.singletonList(RouterUri.class.getName()));
}
}
通过使用Statemenet中的类型占位符 [ $T 是类型替换 $L 是字面量替换(直接替换) $S 是字符串替换 $N 是名称替换 ]
- path
代码块
Java
handler.register($S, $S, $S, $L, L)builder.addStatement(“handler.register($S, $S, $S, $L, L)”,
uri.scheme(),
uri.host(),
path,
handler,
uri.exported(),
interceptors);
可以生成:
然后会生成按如下规则生成Java文件
代码块
Java
buildHandlerInitClass(builder.build(), “UriAnnotationInit” + Const.SPLITTER + hash, Const.URI_ANNOTATION_HANDLER_CLASS, Const.URI_ANNOTATION_INIT_CLASS);
这样在编译时,WMRouter生成了用@RouterUri注解的Activity与其对应的uri注册到UrlAnnotationHandler的代码。这些代码会在UrlAnnotationHandler初始化时调用。 UrlAnnotationHandler生成处理这个uri的UriHandler,这样再调用Router.startUri(),就可以导航到目标界面。
具体使用这个生成好的类的方法
可以看ServiceLoader的初始化方法:
代码块
Java
void doInit() {
Class.forName(“com.sankuai.waimai.router.generated.ServiceLoaderInit”).getMethod(“init”).invoke(null);
}
private static final LazyInitHelper sInitHelper = new LazyInitHelper(“ServiceLoader”) {
@Override
protected void doInit() {
try {
// 反射调用Init类,避免引用的类过多,导致main dex capacity exceeded问题
Class.forName(Const.SERVICE_LOADER_INIT)
.getMethod(Const.INIT_METHOD)
.invoke(null);
Debugger.i("[ServiceLoader] init class invoked");
} catch (Exception e) {
Debugger.fatal(e);
}
}
};
初始化的时候反射调用了ServiceLoaderInit.init()方法。
而ServiceLoaderInit的生成使用到了WMRouterPlugin,参照 #WMRouterTransform 中的注解就可以清楚的看出来
ServiceInit_xxx1 的产生
其实在生成UriAnnotationInit_xx类的时候,同时生成了ServiceInit_xx类
代码块
Java
/**
* 生成类似下面格式的HandlerInitClass,同时生成ServiceInitClass
*
* package com.sankuai.waimai.router.generated;
* public class UriRouter_RouterUri_xxx implements IUriAnnotationInit {
* public void init(UriAnnotationHandler handler) {
* handler.register("", “”, “/login”, “com.xxx.LoginActivity”, false);
* // …
* }
* }
*
*
* @param code 方法中的代码
* @param genClassName 生成class的SimpleClassName,形如 UriRouter_RouterUri_xxx
* @param handlerClassName Handler类名,例如 com.sankuai.waimai.router.common.UriAnnotationHandler
* @param interfaceName 接口名,例如 com.sankuai.waimai.router.common.IUriAnnotationInit
/
public void buildHandlerInitClass(CodeBlock code, String genClassName, String handlerClassName, String interfaceName) {
MethodSpec methodSpec = MethodSpec.methodBuilder(Const.INIT_METHOD)
.addModifiers(Modifier.PUBLIC)
.returns(TypeName.VOID)
.addParameter(className(handlerClassName), “handler”)
.addCode(code)
.build();
TypeSpec typeSpec = TypeSpec.classBuilder(genClassName)
.addSuperinterface(className(interfaceName))
.addModifiers(Modifier.PUBLIC)
.addMethod(methodSpec)
.build();
try {
JavaFile.builder(Const.GEN_PKG, typeSpec)
.build()
.writeTo(filer);
} catch (IOException e) {
throw new RuntimeException(e);
}
String fullImplName = Const.GEN_PKG + Const.DOT + genClassName;
String className = “ServiceInit” + Const.SPLITTER + hash(genClassName);
new ServiceInitClassBuilder(className)
.putDirectly(interfaceName, fullImplName, fullImplName, false)
.build();
}
/*
* 辅助工具类,用于生成ServiceInitClass,格式如下:
*
* package com.sankuai.waimai.router.generated.service;
*
* import com.sankuai.waimai.router.service.ServiceLoader;
*
* public class <ClassName> {
* public static void init() {
* ServiceLoader.put(com.xxx.interface1.class, “key1”, com.xxx.implementsA.class, false);
* ServiceLoader.put(com.xxx.interface2.class, “key2”, com.xxx.implementsB.class, false);
* }
* }
*
*/
public class ServiceInitClassBuilder {
private final String className;
private final CodeBlock.Builder builder;
private final ClassName serviceLoaderClass;
public ServiceInitClassBuilder(String className) {
this.className = className;
this.builder = CodeBlock.builder();
this.serviceLoaderClass = className(Const.SERVICE_LOADER_CLASS);
}
public ServiceInitClassBuilder put(String interfaceName, String key, String implementName, boolean singleton) {
builder.addStatement("T.class, $S, $T.class, KaTeX parse error: Expected 'EOF', got '}' at position 228: … this; }̲ publ…L拼接原始字符串
builder.addStatement(“T.class, $S, $L.class, $L)”,
serviceLoaderClass,
className(interfaceName),
key,
implementName,
singleton);
return this;
}
public void build() {
MethodSpec methodSpec = MethodSpec.methodBuilder(Const.INIT_METHOD)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(TypeName.VOID)
.addCode(this.builder.build())
.build();
TypeSpec typeSpec = TypeSpec.classBuilder(this.className)
.addModifiers(Modifier.PUBLIC)
.addMethod(methodSpec)
.build();
try {
JavaFile.builder(Const.GEN_PKG_SERVICE, typeSpec)
.build()
.writeTo(filer);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
而像 ServiceInit_xxx1 类的内容可以看下图
代码块
Java
public class ServiceInit_eb71854fbd69455ef4e0aa026c2e9881 {
public static void init() {
ServiceLoader.put(IUriAnnotationInit.class, “com.sankuai.waimai.router.generated.UriAnnotationInit_72565413b8384a4bebb02d352762d60d”, com.sankuai.waimai.router.generated.UriAnnotationInit_72565413b8384a4bebb02d352762d60d.class, false);
}
}
其实 ServiceInit_xx1.init的作用就是把接口与实现类的关系保存到ServiceLoader中。
所以 ServiceLoaderInit.init被调用后,SerciceLoader中就保存了IUriAnnotationInit的接口实现类UriAnnotationInit_xx。
通过SerciceLoader来获取IUriAnnotationInit的实现类,并调用其init方法。
代码块
Java
/**
- 使用ServiceLoader加载注解配置
- Created by jzj on 2018/4/28.
*/
public class DefaultAnnotationLoader implements AnnotationLoader {
public static final AnnotationLoader INSTANCE = new DefaultAnnotationLoader();
@Override
public void load(T handler,
Class<? extends AnnotationInit> initClass) {
List<? extends AnnotationInit> services = Router.getAllServices(initClass);
for (AnnotationInit service : services) {
service.init(handler);
}
}
}
service.init(handler)实际上就是调用下面的代码