1. 基本原理
Arouter是一套开源的路由框架。
在模块化的开发中,不同模块的 Activity如果想要显式的 Intent跳转的话,需要模块之间的互相引用,否则只能隐式Intent跳转。 Arouter实现的就是在模块之间不引用的情况下实现显式的跳转
基本原理就是在每个模块中生成路由的映射关系,等到可以引用再注册到Arouter中,这个时候不同的模块之间已经有了互相引用,互相之间的调用就没了问题,简单说就是将引用延迟
2. 代码生成
Arouter使用APT搭配 JavaPoet生成代码,定义编译期的注解,再通过继承Proccesor实现代码生成逻辑,实现了编译期生成代码的逻辑
路由代码生成逻辑在 RouterProcess文件中,入口 process()中,拿到所有的 Route注解,调用parseRoutes()方法生成代码。
2.1. 组装路由元数据
在parseRoutes()方法中,第一块是解析 Route注解,组装 RouterMeta路由元数据,以 Activity为类
- 遍历
Route注解 - 判断是否是
Activty - 解析其中的
AutoWired注解 - 单路由组装单个
RouteMeta,多路由组装多个RouteMeta - 分组缓存
RouteMeta
2.2. 分组缓存
将上一步组装的 RouteMeta分组缓存到 groupMap中。
routeVerify()中检测和解析path,根据分号截取group- 根据
group分组缓存到groupMap中
2.3. 生成分组类
遍历上一步缓存的 groupMap,生成对应 Java文件。
- 遍历
AutoWried数据,生成传参相关代码,这里的Map存储的参数名称以及参数类型的枚举 - 将参数信息以及
RouteMeta代码写入loadInfo()方法 - 生成
Java文件,文件名格式Arouter$$Group$$***,生成的文件包名固定 - 将文件名缓存到
rootMap
2.4. 生成入口类
生成一个 root类,加载上面生成的分组类。文件名格式 Arouter$$Root$$***
3. 路由注册
Arouter需要在App启动的时候进行初始化操作,加载前面生成的Java类,注册完成后会启动拦截器,这里没有介绍
3.1. 注册逻辑
路由注册的逻辑 LogisticsCenter的 init()方法当中
- 获取对应包名的文件,这里的包名就是前面生成的对应包名,如果有缓存的话直接读取缓存
- 文件名反射创建对应对象,并根据文件名的不同调用对应方法,初始化
Root,Interceptor,Provider存入到Warehouse
3.2. 文件遍历
上一步获取对应包名文件的方法 getFileNameByPackageName()
- 读取所有的dex文件目录
- 启动线程池分别遍历每一个dex
- 遍历其中所有的文件,判断是不是对应的包名
- 挂起等待所有线程运行完毕
3.3. 耗时问题
上面这个方法给人的感觉就是耗时,而且还放在app启动的时候调用,势必会增加启动耗时,官方也发现了这个问题,提供了一个解决方案 arouter-register
arouter-register采用 AutoRegister框架实现字节码插桩,在编译的时候将生成类注入到初始化代码中,启动时不再需要扫描所有文件
- 注入的地方是在
LogisticsCenter的loadRouterMap(),初始话的时候会首先执行这个方法,然后判断registerByPlugin的值,如果已经赋值过了,就不会走扫描的逻辑 - 注入的方法就是
register(),根据类名去初始化 - 最终注册的时候先调用
markRegisterByPlugin()给registerByPlugin赋值 - 调用创建的类的方法,将路由信息注册到
Warehouse
4. 路由跳转
前面的准备工作完成之后,就可以下面的代码实现一个简单的路由的跳转
ARouter.getInstance().build(*****).navigation(context)
4.1. 解析路由
build()方法最终走到 _Arouter中,这里做路由替换逻辑和路由的解析
- 获取路由替换的服务,如果有就调用替换路由
- 解析路由,用分号分离出分组名称
- 创建
Postcard返回
4.2. 读取路由
前面成功创建了 Postcard对象,并调用它的 navigation()方法,最终走到了 _Arouter.navigation()方法中,其中第一步的处理就是调用 LogisticsCenter.completion(),根据路由的名称,读取出真实的路由数据
- 在
routes中查找缓存,找不到就尝试加载 - 在
groupsIndex中查找group的缓存,前面有提到过这里存储的是生成的分组类信息 - 拿到对应类信息之后创建对象并调用方法,将组内所有的页面缓存到
routes中 - 创建成功后直接再次调用当前方法,走有缓存的逻辑
- 这一步之后是成功拿到了缓存的操作,首先将路由信息存入到
postcard - 如果是uri的话进行解析
- 如果是
Provider类型,就查对应的缓存,如果没有,创建-初始化-存入缓存 - 如果不是
Activity,最终都会调用greenChannel()方法,跳过拦截器
4.3. 路由拦截
完成上面的读取路由回到 _Arouter中,会走到下面代码,首先是根据刚才设置的参数判断是否需要拦截器,拦截器回调或者不需要拦截器直接走 _navigation()做路由跳转
这里的 interceptorService是内部的维护的拦截控制服务,逻辑在 InterceptorServiceImpl中
- 创建计数器,防止拦截器没有调用回调,导致部分拦截器没执行
- 调用
_excute()加载拦截器执行拦截逻辑 - 挂起等待,默认300s(???)
- 执行完成后回调给外面
- 读取
interceptors缓存,前面提到过在afterInit()中会初始化拦截器,这里的缓存是所有的拦截器对象 - 拦截器的回调是继续走下一个拦截器
4.4. 路由跳转
拦截器逻辑结束之后,就可以做最终的路由跳转了,回到 _Arouter的 _navigation()方法
- 创建
Intent - 调用跳转代码
- 类型是
provider直接返回对象 - 类型是
Fragment塞入参数返回对象