ARouter 源码分析1

简介: ARouter 源码分析

ARouter 源码分析

ARouter基本使用

在开始分析源码之前,先了解一下ARoute如何使用的,使用ARoute可以概括为以下3步:

  1. 项目中引入ARouter 及配置
  2. 初始化ARouter
  3. 开始使用

下面详细的看下每一步怎么操作

项目中引入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。这里也可以分为两步:

  1. Test1Activity添加注解。
  2. 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 项目的代码结构,如下

94901cccfc9dd67d1c567136aba8b1a.png

红框内的是ARouter的核心代码,为了方便理解,我画了一个ARouter代码架构图,如下

421e70829bdd30fe5d7e7fbf2cf467c.png

可以发现,ARouter项目主要是围绕着生成和加载及解析路由表来编写的,现在已经对ARouter架构有了基本的印象,下面我们再看下,ARouter是怎么通过上面的架构来实现路由跳转的,

4a322d6cf5b0f9ae3448f24864d3ea0.png

从这幅图中,可以更加清晰的理解ARouter每个模块的职责与联系,当然到这里,也仅是列出了ARouter项目的架构和模块间的联系,还没有对ARouter整体的工作流程有个基础的认识,下面会介绍一下ARouter的工作流程。

ARouter整体的工作流程

一图胜千言,这里还是先用图来展示ARouter的工作流程,如下

dee77155c531497bbba93a50e2305f9.png

这里只是ARouter跳转的主要流程,好多细节方面的知识下文会讲解,在深入源码分析之前,对流程先有个印象,方便下文源码的理解,也防止在源码的大海里迷失方向。

ARouter原理解析

到这里我们就正式开始ARouter的源码分析,为了防止在源码的大海里迷失,分析源码的顺序就按上面画出的工作流程图来一步步进行,首先看下路由文件的生成原理。

路由文件的生成

路由文件的生成是通过APT技术来实现的,如果不了解APT技术可以先去了解一下,不然这部分代码可能看不懂,不过最后会画一张这部分的流程图,方便理解记忆。

生成路由文件的主要包目录如图

5c8752d2aad2ee584a70e99515fa5b1.png

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,这个文件的位置如下图

495566038bcdec088567de691860720.png

可以看到生成的代码,是创建了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确实是用来保存路由的元信息的,这里大家熟悉一下每个成员变量的作用,下文源码分析的时候还会出现。路由文件的生成原理就到这里,接着看下代码在运行时是如何加载路由表以及如何根据路由表的信息来做跳转的。


相关文章
|
存储 算法
TreadLocal源码分析
TreadLocal源码分析
Flutter源码分析笔记:Widget类源码分析
本文记录阅读与分析Flutter源码 - Widget类源码分析。
99 0
Flutter源码分析笔记:Widget类源码分析
|
ARouter
ARouter 源码分析2
ARouter 源码分析
vivid源码分析
vivid源码分析
117 0
|
存储 Java 应用服务中间件
SpringMVC源码分析 RequestContextHolder使用与源码分析
SpringMVC源码分析 RequestContextHolder使用与源码分析
SpringMVC源码分析 RequestContextHolder使用与源码分析
|
iOS开发
fishhook源码分析
最早了解到[fishhook](https://github.com/facebook/fishhook)是看了下面两篇文章之后,顿时让我觉得这是一个非常好的东西。总共210行代码,收获了1500+个star,神作啊。 1. [iOS Lazy Binding](http://www.atatech.org/articles/68014),使用fishhook拦截NSSetUncaughtE
2447 0
|
移动开发 Java 开发者
Stresstester源码分析
stresstester-1.0.jar是早期淘宝的一个压力测试工具,很方便开发人员进行本地代码的压力测试,其他专门压力测试工具也有很多,如:jmeter loadrunner 等等,本篇文章主要讲一下stresstester的源码设计
10621 0
|
数据库 Android开发
VirtualApk源码分析-ContentProvider插件化
android通过ContentProvider可以实现进程间的数据共享,例如APP通过MediaProvider可以访问多媒体数据库的内容。通常我们在Activity通过getContentResolver().
1374 0
|
Android开发
VirtualApk源码分析-BroadcastReceiver插件化
广播的注册分为静态注册和动态注册两种,android关于广播的源码分析可以参照此文 : https://www.jianshu.com/writer#/notebooks/11066526/notes/28225946 广播的插件化指的是插件能够接受到外部的广播,即插件apk里的Receiver对象能够响应对应的广播。
1371 0
VirtualApk源码分析-Activity插件化
插件以APK的形式保存在SD卡上,通过startActivity方式启动Activity需要首先将Activity注册到AndroidManifest.xml,如果没有注册就会出现如下错误。
1085 0