ARouter 源码分析
ARouter基本使用
在开始分析源码之前,先了解一下ARoute如何使用的,使用ARoute可以概括为以下3步:
- 项目中引入ARouter 及配置
- 初始化ARouter
- 开始使用
下面详细的看下每一步怎么操作
项目中引入ARouter及配置
其实这一步就是导包,将ARouter 下载到本地,在app module下的build.gradle
文件中 添加以下代码
plugins { ... id 'kotlin-kapt' } android { ... kapt { arguments { arg("AROUTER_MODULE_NAME", project.getName()) } } ... } dependencies { implementation 'com.alibaba:arouter-api:x.x.x' kapt 'com.alibaba:arouter-compiler:x.x.x' ... }
初始化ARouter
初始化很简单,只需要在项目的application类(我的是MyApplication)中添加下面代码即可
kotlin 复制代码 class MyApplication : Application() { override fun onCreate() { super.onCreate() // 这两行必须写在init之前,否则这些配置在init过程中将无效 ARouter.openLog() // 打印日志 ARouter.openDebug() // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险) ARouter.init(this) // 尽可能早,推荐在Application中初始化 } }
开始使用
只是界面跳转,使用起来还是挺简单地,我这里写了1个Activity,Test1Activity
,要实现的功能就是从MainActivity
跳转到Test1Activity
。这里也可以分为两步:
Test1Activity
添加注解。MainActivity
添加跳转代码。
Test1Activity
添加的注解如下4
// 在支持路由的页面上添加注解(必选) // 这里的路径需要注意的是至少需要有两级,/xx/xx @Route(path = "/test/Test1Activity") class Test1Activity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_test1) } }
MainActivity
添加的跳转代码如下
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) findViewById<TextView>(R.id.tv_test).setOnClickListener { // 1. 应用内简单的跳转(通过URL跳转在'进阶用法'中) ARouter.getInstance().build("/test/Test1Activity").navigation() } } }
做完了上面的三步,就可以实现利用ARoute进行界面跳转的功能了。ARouter 的功能还是比较多的,可以点击这里,进行详细了解。
ARouter概览
经过上面的步骤,我们已经可以在项目中使用ARoute了,下面我们来看下ARouter项目的整体架构和实现路由跳转的主要流程。
ARouter 整体架构
ARouter 项目的代码结构,如下
红框内的是ARouter的核心代码,为了方便理解,我画了一个ARouter代码架构图,如下
可以发现,ARouter项目主要是围绕着生成和加载及解析路由表来编写的,现在已经对ARouter架构有了基本的印象,下面我们再看下,ARouter是怎么通过上面的架构来实现路由跳转的,
从这幅图中,可以更加清晰的理解ARouter每个模块的职责与联系,当然到这里,也仅是列出了ARouter项目的架构和模块间的联系,还没有对ARouter整体的工作流程有个基础的认识,下面会介绍一下ARouter的工作流程。
ARouter整体的工作流程
一图胜千言,这里还是先用图来展示ARouter的工作流程,如下
这里只是ARouter跳转的主要流程,好多细节方面的知识下文会讲解,在深入源码分析之前,对流程先有个印象,方便下文源码的理解,也防止在源码的大海里迷失方向。
ARouter原理解析
到这里我们就正式开始ARouter的源码分析,为了防止在源码的大海里迷失,分析源码的顺序就按上面画出的工作流程图来一步步进行,首先看下路由文件的生成原理。
路由文件的生成
路由文件的生成是通过APT技术来实现的,如果不了解APT技术可以先去了解一下,不然这部分代码可能看不懂,不过最后会画一张这部分的流程图,方便理解记忆。
生成路由文件的主要包目录如图
APT技术就是对特定的注解来做一些逻辑处理和自动生成文件,上图标出的Route
就是注解,RouteProcessor
就是用来处理注解的注解处理器,现在看下RouteProcessor
主要代码,从程序入口开始看
@Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { if (CollectionUtils.isNotEmpty(annotations)) { //拿到所有Route注解修饰的类 Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class); try { this.parseRoutes(routeElements); } catch (Exception e) { logger.error(e); } return true; } return false; }
接着看下parseRoutes
方法的代码,如下
private void parseRoutes(Set<? extends Element> routeElements) throws IOException { ... // Write root meta into disk. String rootFileName = NAME_OF_ROOT + SEPARATOR + moduleName; JavaFile.builder(PACKAGE_OF_GENERATE_FILE, TypeSpec.classBuilder(rootFileName) .addJavadoc(WARNING_TIPS) .addSuperinterface(ClassName.get(elementUtils.getTypeElement(ITROUTE_ROOT))) .addModifiers(PUBLIC) .addMethod(loadIntoMethodOfRootBuilder.build()) .build() ).build().writeTo(mFiler); ... }
上面的是parseRoutes
方法的部分代码,此方法的主要作用就是生成路由文件,生成的路由文件的格式如下
public class ARouter$$Group$$test implements IRouteGroup { @Override public void loadInto(Map<String, RouteMeta> atlas) { atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("ch", 5); put("fl", 6); put("dou", 7); put("boy", 0); put("url", 8); put("pac", 10); put("obj", 11); put("name", 8); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648)); atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2", "test", new java.util.HashMap<String, Integer>(){{put("key1", 8); }}, -1, -2147483648)); atlas.put("/test/activity3", RouteMeta.build(RouteType.ACTIVITY, Test3Activity.class, "/test/activity3", "test", new java.util.HashMap<String, Integer>(){{put("name", 8); put("boy", 0); put("age", 3); }}, -1, -2147483648)); atlas.put("/test/activity4", RouteMeta.build(RouteType.ACTIVITY, Test4Activity.class, "/test/activity4", "test", null, -1, -2147483648)); atlas.put("/test/fragment", RouteMeta.build(RouteType.FRAGMENT, BlankFragment.class, "/test/fragment", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("pac", 10); put("ch", 5); put("obj", 11); put("fl", 6); put("name", 8); put("dou", 7); put("boy", 0); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648)); atlas.put("/test/webview", RouteMeta.build(RouteType.ACTIVITY, TestWebview.class, "/test/webview", "test", null, -1, -2147483648)); } }
我用的是官方的demo,这个文件的位置如下图
可以看到生成的代码,是创建了RouteMeta
实例,然后放到map中,那么这个RouteMeta
是什么呢?在路由跳转中又有什么作用呢?
RouteMeta是什么?
从名字上来看是路由的元数据,可以猜测此类包含了路由的元信息,那么这个类是不是这样呢?看下代码
public class RouteMeta { private RouteType type; // Type of route private Element rawType; // Raw type of route private Class<?> destination; // Destination private String path; // Path of route private String group; // Group of route private int priority = -1; // The smaller the number, the higher the priority private int extra; // Extra data private Map<String, Integer> paramsType; // Param type private String name; private Map<String, Autowired> injectConfig; // Cache inject config.
上面的代码是RouteMeta
的成员变量,这里我做了一个表格来解释每个成员变量的作用,如下
成员变量 | 释义 |
type | 路线类型:路线类型 RouteType 是一个枚举类,类型有这几个:Activity、Service、Provider、ContentProvider、Fragment、Broadcast、Method、Unknown |
rawType | 路线原始类型:路由处理器 RouteProcessor 设定 |
destination | 终点:声明了 @Route 的跳转目标的 Class ,比如目标 Activity 和 Fragment 的 Class,由 RouteProcessor 设定的。 |
path | 路径:比如 path = /goods/details ,那么 goods 就是 group ,details 就是路径 path |
group | 路线组:如果在@Route注解中没有设置,那么就从设置的path中取值,设置的话就用设置的 |
priority | 优先级:优先级在 @Route 中无法设定,是给拦截器用的,priority 的值越小,拦截器的优先级就越高 |
extra | 标志:路由文档 RouteDoc 的标志 |
paramsType | 参数类型:对于我们跳转时设定的参数,ARouter 会根据不同的类型给它们一个枚举值,然后取值时,再根据不同的类型调用 Intent 的 getXXXExtra() 等方法 |
name | 路线名称 |
injectConfig | 注入配置 |
从上面的表格中,可以看出RouteMeta
确实是用来保存路由的元信息的,这里大家熟悉一下每个成员变量的作用,下文源码分析的时候还会出现。路由文件的生成原理就到这里,接着看下代码在运行时是如何加载路由表以及如何根据路由表的信息来做跳转的。