摘要
最近在学习项目组件化时用到了ARouter,本文先介绍ARouter的特性及基本使用,然后从源码的角度分析ARouter的实现原理。
注意本文分析的版本分别为:
- arouter-api,1.5.0;
- arouter-compiler,1.2.2
特性
ARouter官方说是帮助App进行组件化改造的框架——支持模块间的路由、通信、解耦。其具有以下特点(内容来自官方文档):
- 支持直接解析标准URL进行跳转,并自动注入参数到目标页面中,如
arouter://m.aliyun.com/test/activity3?name=alex&age=18&boy=true&high=180
; - 支持多模块工程使用;
- 支持添加多个拦截器,自定义拦截顺序;
- 支持依赖注入,可单独作为依赖注入框架使用;
- 支持InstantRun;
- 支持MultiDex(Google方案);
- 映射关系按组分类、多级管理,按需初始化
- 支持用户指定全局降级与局部降级策略
- 页面、拦截器、服务等组件均自动注册到框架
- 支持多种方式配置转场动画
- 支持获取Fragment
- 完全支持Kotlin以及混编(配置见文末 其他#5)
- 支持第三方 App 加固(使用 arouter-register 实现自动注册)
- 支持生成路由文档
- 提供 IDE 插件便捷的关联路径和目标类
基本使用
源码分析
arouter-api
提供ARouter的API实现,如我们使用ARouter的入口ARouter.getInstance()
及常用的一些调整配置方法,都定义在这。
arouter-api的结构
1 | com.alibaba.android.arouter.api |
这里着重看下LogisticsCenter.java 中的init方法,因为在ARouter.getInstance().init()最终会走到这个方法:
_Arouter.java的init方法
1 | protected static synchronized boolean init(Application application) { |
LogisticsCenter.java的init方法
1 | public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException { |
上面方法其实等同于Logistics.register方法,只不过register方法是由插件调用的,看看register方法的实现:
1 | private static void register(String className) { |
方法比较简单,至于到底注册了什么,将在后面分析ARouter-compiler时具体分析。
在使用ARouter时,最终都会调用navigation()
方法,而这个navigation
有两种类型的实现:
- 不带页面跳转的
- 带页面跳转的
先看看不带页面跳转的,其方法如下:
1 | // 这个方法最终户返回一个实现了IProvider的服务的实例,获取该实例后可以调用该服务中定义的方法(服务中使用context对象也是可以的,因为IProvider提供了Context对象的初始化方法。 |
在看看带页面跳转的,其方法如下:
1 | // 带页面跳转的navigation |
arouter-compiler
arouter-compiler
主要负责ARouter定义的注解的解析,关于注解,可以参考文章Java 注解详解在分析之前,需要先了解下ARouter定义了哪些注解,见后面的arouter-annotation小结。
ARouter采用编译时注解,并通过Javapoet库来自动生成代码。
arouter-compiler的结构及说明
1 | com.alibaba.android.arouter.compiler |
BaseProcessor.java
提供了注解处理需要的一些工具类如Filer
、Logger
、Types
、Elements
、TypeUtils
等,init
方法中对这些工具类进行了初始化。
RouteProcessor.java
ARouter的核心,核心实现在process()
方法中的parseRoutes()
方法中,现在对parseRoutes()
方法做简单分析。
1 | private void parseRoutes(Set<? extends Element> routeElements) throws IOException { |
通过仔细分析上面的源码,可以发现他会生成如下几个文件(按顺序):
- ARouterGroupName.java(GroupName为
@Route
注解的第一个 “/“ 后面的内容); - arouter-map-of-ModuleName.json(当前模块的json格式文档)
- ARouterModuleName.java(ModuleName为当前的module名称);
- ARouterModuleName.java(ModuleName为当前的module名称);
上面的三个java文件生成的同时,对应的会生成相应的loadInto方法,这些方法在aroute-api模块些的LogisticsCenter.init方法中被调用,这样就完成了注册工作。下面看看ARouter生成的Group文件
1 | package com.alibaba.android.arouter.routes; |
当loadInfo方法别调用后,参数atlas中的值就都存储在了Warehouse中对应的数据结构中了。而Warehouse中的内容会在LogisticsCenter的completion方法调用的时候存储到Postcard(RouteMeta子类)对象中,调用的时机是ARouter的navigation方法。
AutowiredProcessor.java
在介绍Autowired注解之前,先看看使用Autowired注解使用的方法:
- 初始化ARouter
- 构建参数并跳转,具体如下:
1 | ARouter.getInstance().build("/test/activity1") |
- 在路由为
/test/activity1
的Activity中,对相应的Filed加上@Autowired注解,如:
1 |
|
- 在该Activity(设为Test1Activity)的onCreate中调用ARouter.getInstance().inject()方法。
- 在编译过后,ARouter会为我们生产一个名为Test1ActivityAutowired的文件,其内容如下:
1 | /** |
在这个Inject方法中,完成了被注解的字段的赋值,接下来我们就可以直接使用这些值了。那么这个inject()
方法在什么时候被调用呢?看代码:
1 |
|
在AutowiredService的实现类AutowiredServiceImpl的autowire方法中,调用了这个方法,这个类做了以下几件事:
- 实现了参数的绑定(即调用Atowired注解生成的类的的inject方法)
- 对ISyringe对象(也就是Autowired注解生成的类)做了缓存,采用LRUCache算法实现;
- 实现了黑名单策略,黑名单中的类不会进行参数的绑定
注意,这个AutowiredServiceImpl也采用了@Route注解,说明我们可以通过ARoute.getInstance().build(“path”).navigation()来获取。开代码确实是通过这种方式获取的。调取最终发生在_ARouter的inject(Object obj)中,代码如下:
1 | static void inject(Object thiz) { |
上面结合源码分析了下Autowired注解参数的绑定过程,现在来具体分析AutowiredProcessor,看看他是如何生成对应的ARouter$$Autowired类的。主要处理逻辑在process方法中
1 |
|
需要注意一点的是,对象参数的传递用到了SerializationService
的parseObject来实现的,现将要传递的参数转换成序列换从json字符串,再在使用的时候反序列化成相应的对象来实现的。
generateHelper()是具体生成代码的实现,主要使用的是JavaPoet,这里不再赘述。
InterceptorProcessor.java
ARouter的一大特点就是支持自定义多个拦截器,并支持自定义顺序,那么这是怎么实现的呢。
_ARouter的navigation关于拦截器的处理:
1 | if (!postcard.isGreenChannel()) { // 拦截操作需要在异步线程中操作,不然会ANR |
看看InterceptorService实现类:
1 | public class InterceptorServiceImpl implements InterceptorService { |
对上面代码做几点说明:
- 拦截器的同步执行是通过CountDownLatch来实现的;
- 拦截器的顺序(优先级)有init方法中来实现,init方法中会迭代Warehouse.interceptorsIndex(这是一个UniqueTreeMap结构,它是有序的)
arouter-annotation
arouter-annotation的结构
1 | com/alibaba/android/arouter/facade |
总结
本文篇幅较长,从使用入手,借助ARouter Demo生成的文件从源码入手,分析了ARouter的具体实现,主要流程其实如下:
- 使用注解 编译,生成相应的类;
- 调用初始化方法,初始化方法最后会LogisticsCenter.init方法,这个方法中会调用编译生成类的loadInfo方法,将路由信息、拦截器信息、Group信息加载到Warehouse中(内存中)
- 上面的loadInfo完成了路由path的映射,同时完成了RouteMeta的创建;
- 在调用ARouter.getInstance().navigation()方法时,LogisticsCenter.completion方法会将RouteMeta转换为Postcard,完善信息,然后最终交由_navigation进行跳转的处理
- 对于@Autowired注解,注解处理器处理后,在调用ARouter.getInstance().inject()后,会完成参数的绑定。
ARouter Activity跳转本质上还是调用startActivity方法的。传递的信息都在Postcard中。