ARouter
概述
在每个需要对其他module提供调用的Activity中,都要声明类似下面@Route注解,我们称之为路由地址。
@Route(path = "/main/main")
public class MainActivity extends AppCompatActivity {
}
@Route(path = "/module1/module1main")
public class Module1MainActivity extends AppCompatActivity {
}
现在我们有三个module
- APP——主工程
- QRCode——扫码Module
- Gallery——项目模块
对于其他module需要使用ARouter跳转至QRCode Activity时
我们需要声明一个QRCode Activity的路由地址
public static final String QRCODE_SCAN_PATH = "/qrcode/scan";
然后在对应的QRCode的Activity上添加注解
@Route(path = RouteHub.QRCode.QRCODE_SCAN_PATH)
然后调用以下方法就可以完成跳转了
ARouter.getInstance().buildRouteHub.QRCode.QRCODE_SCAN_PATH).navigation(this)
这样,两个模块不用相互有任何直接的依赖,就可以进行转跳,模块与模块之间就相互独立了。
原理
APT
ARouter的使用非常方便,得益于APT。
APT的作用是在编译阶段扫描并处理代码中的注解,然后根据注解输出Java文件。
ARouter为了方便实现注解处理器还额外用了两个库。
一个是JavaPoet,他提供了调用对象方法的方式生成需要的代码,而不再需要人为的用StringBuilder去拼接代码,再使用IO写入文件。
第二个是Auto-Service,他提供了简便的方式去注册APT,避免了原本繁琐的注册步骤。
@Route
Route的定义:
@Target({
ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route{
/**
* Path of route
*/
String path();
……
}
- @Target({ElementType.TYPE})——表示这个annotation是修饰类的
- @Retention(RetentionPolicy.CLASS)——表示需要保留到编译时
Route中有一个主要的参数path,他表示了Activity的路由地址。
@Route(path = RouteHub.QRCode.QRCODE_SCAN_PATH)
这样编译时能获取到Route所注解的类,并且能获取到path路径。
RouteProcessor
RouteProcessor是对@Route处理的地方。
@AutoService(Processor.class)
@SupportedAnnotationTypes({
ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
public class RouteProcessor extends BaseProcessor
解释
- auto-service——这个库为Processor完成了自动注册
- @SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})——表明了当前Processor是出里那些注释的
RouteProcessor继承于BaseProcessor,在init方法中获取到了每个模块的moduleName。
// Attempt to get user configuration [moduleName]
Map<String, String> options = processingEnv.getOptions();
if (MapUtils.isNotEmpty(options)) {
moduleName = options.get(KEY_MODULE_NAME);
…………
}
RouteProcessor的process方法是对注解处理的地方,它直接获取了所有Route注解的元素。
Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
拿到被标注的元素后就会进入this.parseRoutes(routeElements);方法。这个方法使用JavaPoet生成Java文件。如果不用这个库我们也可以使用StringBuilder去写Java文件的内容。
我们先来看看他生成的类。
IRouteGroup
public class ARouter$$Group$$qrcode implements IRouteGroup {
@Override
public void loadInto(Map<String,RouteMeta> atlas) {
atlas.put("/qrcode/scan",
RouteMeta.build(
RouteType.ACTIVITY,
ScanQRActivity.class,
"/qrcode/scan",
"qrcode",
new java.util.HashMap<String,Integer>(){
{
put("tag", 8); }}, -1, -2147483648));
}
}
关于RouteMeta: RouteMeta是包含了@Route所注解的元素的必要信息,最明显的就是ScanQRActivity.class,有了它,我们就可以通过Intent跳转到这个Activity了。
ARouter$$Group$$qrcode
这个类继承自IRouteGroup ,他实现了接口中的loadInto方法。
loadInto方法逻辑很简单,传入一个map,将注解的path值作为key,将元素(RouteMeta)作为value作为Value放入map。
如果完成了这个方法,就完成了Activity的注册。
IRouteRoot
RouteProcessor还产生了ARouter$$Root$$qrcode
这个类
public class ARouter$$Root$$qrcode implements IRouteRoot {
@Override
public void loadInto(Map<String,Class<? extends IRouteGroup>> routes) {
routes.put("qrcode",ARouter$$Group$$qrcode.class);
}
}
它实现了IRouteRoot接口,内容非常相似。通过loadInto方法,往Map中插入以group名为Key,IRouteGroup实现类为Value的内容。
group默认就是path中第一个斜杠之后的内容(@Route(path="/group/xxx"))
如果调用了这个方法,那么可以通过group拿到IRouteGroup实现类的class,有了class实例化之后就能通过前面所说的拿到Activity.class了。
整体的结构是下图的样子
我们看到,其实直接可以从IRouteGroup 传入的Map获得我们的RouteMeta,那么为什么还需要IRouteRoot的loadInto再把IRouteGroup放到Map里呢。这个大家后面就会知道。
process
回过头来再看RouteProcessor的process方法:
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironmentroundEnv) {
if(CollectionUtils.isNotEmpty(annotations