0%

Android 三方库源码解析系列(二):ARouter 原理及源码解析

前言

1. ARouter 简介

  • ARouter 是阿里开源的一款帮助 Android App 进行组件化改造的路由框架,是 Android 平台中对页面、服务提供路由功能的中间件,可以实现在不同模块的 Activity 之间跳转
  • ARouter 的特点是灵活性强以及帮助项目解耦
  1. 灵活性强
    • 在一些复杂的业务场景下,很多功能都是运营人员动态配置的
    • 比如电商系统需要下发一个活动页面,App 事先不知道该活动具体的目标页面,但如果提前做好了页面映射,就可以自由配置了
  2. 项目解耦
    • 随着业务量增长,App 项目代码会越来越多,开发人员之间的协作也会变得越来越复杂,而解决这个问题的方案一般就是插件化和组件化
    • 插件化和组件化的前提是解耦,解耦后还要保持页面之间的依赖关系,这时就需要一套路由机制了

2. ARouter 架构概览

ARouter 架构概览

  • API:API 模块由下面几个子模块组成

    API

    • launcher:包含了启动器 ARouter
    • core:包含物流中心 LogsticsCenter 和仓库 Warehouse 等类
    • exception:异常
    • thread:拦截器链是放在子线程中执行的,用到了这个模块的 CancellableCountDownLatch
    • facede:这个模块包含了 NavigationCallback 和 IInterceptor 等接口
    • utils:包含了 ARouter 自定义的日志打印器等工具类
    • base:只有一个用于保存拦截器的 UnitqueKeyTreeMap
  • Compiler

    Compiler

    • ARouter 的 Compiler 模块是用于生成路由表的,@Autowired、@Interceptor 和 @Route 注解对应的注解处理器分别是是 AutowiredProcessor、InterceptorProcessor 以及 RouteProcessor
  • Plugin

    Plugin

    • 如果我们使用了 ARouter 的路由表加载插件,那这个路由表就会由 ARouter Register 插件加载
  • Annotation

    Annotation

    • 这个模块比较简单,只包含了一些注解和枚举类

3. ARouter 基本用法

  1. 添加依赖与配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // Kotlin 注解处理插件
    apply plugin: 'kotlin-kapt'

    // Kotlin 注解处理选项
    kapt {
    // 参数:模块名
    arguments {
    arg("AROUTER_MODULE_NAME", project.getName())
    }
    }

    // 依赖
    dependencies {
    // 替换成最新版本,需要注意的是 api 要与 compiler 匹配使用
    // 均使用最新版本可以保证兼容
    compile 'com.alibaba:arouter-api:1.5.0'
    kapt 'com.alibaba:arouter-compiler:1.2.2'

    // 如果要在 Activity 之间传递序列化对象,就要使用 Json 解析
    implementation 'com.alibaba:fastjson:1.2.48'
    }
  2. 声明路径

    1
    2
    3
    4
    @Route(path = "/goods/details", group = "taobao")
    class GoodsDetailsActivity : AppCompatActivity() {
    // ...
    }
    • 使用 ARouter 要用 @Route 注解声明跳转目标的路径,在这里要注意,最前面的斜杠是不能少的,而且路径至少有量级
    • group 是可选的,ARouter 内部会对 path 进行分组,以上面这段代码为例,如果不传 group 的话,那 ARouter 会把 goods 作为该路径的 group ,否则 taobao 就是该路径所属的 group
  3. 初始化 ARouter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    private fun initARouter() {
    if(BuildConfig.DEBUG) {
    // 打印日志
    ARouter.openLog()

    // 开启调试模式(如果在 InstantRun 模式下运行
    // 线上版本需要关闭,否则有安全风险)
    ARouter.openDebug()
    }

    // 尽可能早初始化,推荐在 Application 中初始化
    ARouter.init(application)
    }
    • 打印日志和开启调试模式必须写 在 init() 之前,否则这些配置在初始化过程中将无效
  4. 跳转

    1
    2
    3
    4
    5
    // 1. 应用内简单的跳转
    ARouter.getInstance()
    // 构建明信片,设定 path 与 group
    .build("/goods/details", "taobao")
    .navigation()
    • 在跳转时,我们也可以传 group,比如这个例子中传了 taobao,那就会跳转到 taobao 分组下的商品详情页,不传的话,就会跳到 goods 分组下的详情页

一. 明信片 Postcard

  • 当我们调用 ARouter.getinstance().build() 时,其实是在创建一个明信片 Postcard 对象,withXXX()navigation() 方法就是它的方法,Postcard 的 navigation() 方法最终调用的是 ARouter 的 navigation() 方法
  • 按 Postcard 的注释来说,它是路线图的容器,我们可以把它看做是一张包含了收件人信息以及特定内容的明信片
  • Postcard 继承了 RouteMeta,RouteMeta 是路由表的内容,而 Postcard 则包含了在跳转时的传参和动画等信息

1. RouteMate

  • RouteMate 示意图

    RouteMate 示意图

  • RouteMeta 包含了以下字段

    • 路线类型:路线类型 RouteType 是一个枚举类,有下面几种类型(其中 Service、Broadcast 和 Method 是未实现的)

      • Activity
      • Service
      • Provider:也就是自定义服务 IProvider
      • ContentProvider
      • Fragment
      • Broadcast
      • Method
      • Unknown:未知路线
    • 路线原始类型

      • 路由原始类型 rawType 的类型为 Element,关于 Element ,在后面会进一步讲解,这里只需要知道 Element 中包含了跳转目标的 Class 信息,是由路由处理器 RouteProcessor 设定的
    • 终点

      • 终点 destination 就是声明了 @Route 的跳转目标的 Class ,比如目标 Activity 和 Fragment 的 Class,这个信息也是由 RouteProcessor 设定的
    • 路径和路线组

      • 如果我们在 @Route 中只设定了路径,比如 path = /goods/details ,那么 goods 就是 group ,details 就是路径 path
      • 如果我们设置了 @Route 的 group 的值,比如 group = taobao ,那么 path 的 group 就是 taobao
    • 优先级

      • 优先级在 @Route 中无法设定,是给拦截器用的,priority 的值越小,拦截器的优先级就越高
      • 拦截器链的实现类为 InterceptorServiceImpl,它在调用拦截器的 onInteceptor() 方法时,就是按照 priority 的顺序来获取拦截器,然后逐个调用的
    • 标志:这个 extra 是路由文档 RouteDoc 的标志,文档中包含了路由和服务相关的信息 ,默认不会生成对应的文件,如果想生成的话,可以在注解处理参数中设置生成文档

      • 添加参数:如果我们想查看路由文档,可以 AROUTER_MODULE_NAME 后面添加 AROUTER_GENERATE_DOC: "enable"

      • 文档路径:build/generated/source/apt/(debug or release)/com/alibaba/android/arouter/docs/arouter-map-of-${moduleName}.json,路由文档的大致内容如下

        路由文档的大致内容

    • 参数类型

      • paramsType 也就是参数类型,对于我们跳转时设定的参数,ARouter 会根据不同的类型给它们一个枚举值,然后取值时,再根据不同的类型调用 Intent 的 getXXXExtra() 等方法

2. Postcard

  • Postcard 示意图

    Postcard 示意图

  • Postcard 中包含如下字段

    • uri

      1
      2
      3
      4
      val uri = Uri.parse("demo://www.xxx.com/moduleA/second?name=老王")
      ARouter.getInstance()
      .build(uri)
      .navigation()
      • 在 ARouter 中,我们可以用 URI 作为路径跳转,ARouter 会用路径替换服务 PathReplaceService 替换路径,这个服务没有默认实现,如果我们需要为不同的协议和主机替换不同的路径时,就要自己实现这个服务
      • 如果我们没有实现这个服务,那 scheme 和 主机地址等信息是没有意义的,ARouter 默认情况下只会对路径和参数进行解析,在上面的例子中,路径就是 moduleA/second ,参数为 name=老王
    • tag

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      @Interceptor(priority = 8, name = "测试用拦截器")
      class TestInterceptor : IInterceptor {
      override fun init(context: Context?) {
      // 拦截器的初始化,会在 sdk 初始化的时候调用该方法,仅会调用一次
      }

      override fun process(postcard: Postcard?, callback: InterceptorCallback) {
      callback.onInterrupt(RuntimeException("有内鬼,停止交易"));
      }
      }

      ARouter.getInstance()
      .build(path)
      navigation(this, object : NavigationCallback) {
      // 跳转中断
      override fun onInterrupt(postcard: Postcard?) {
      // 有内鬼,停止交易
      val errorMsg = postcard?.tag
      }
      }
      • tag 用于在 NavigationCallback 的 interrupt() 方法中获取异常信息
      • 在拦截器链 InterceptorServiceImpl 中,当我们在自定义的拦截器中调用 onInterrupt() 方法时,InterceptorServiceImpl 会创建一个 HandlerException ,并把它作为 Postcard 的 tag ,然后监听跳转结果的地方,就能在 onInterrupt() 方法中通过获取 Postcard 的 tag 信息来获取异常信息
      • 我们在拦截器中传入的 exception 为空的话,那么 HandlerException() 的 message 默认为 “No Message.”
    • mBundle

      • 我们调用 withString() 等方法设置要传递给跳转目标的数据时,这个数据就是放在 mBundle 中的
    • flags

      • 我们调用 withFlag() 设定 Activity 的启动标志时,这个标志就会赋值给 flags 字段
    • timeout

      • 拦截器链处理跳转事件是放在 CountDownLatch 中执行的,超时时间默认为 300 秒,也就是 5 分钟,所以我们在拦截器中不要进行时间太长的耗时操作
    • provider

      • 当我们实现了自定义服务时,参数注解处理器 AutowiredProcessor 会为各个路径创建一个实现注射器 ISyringe 接口的类,在这个类的 inject() 方法中,调用了 ARouter.getInstance().navigation(XXXService.class),当 LogisticsCenter 发现这是一个 Provider 时,就会通过反射创建一个 Provider 实例,然后设置给 Postcard ,再进行跳转
    • greenChannel

      • 所谓绿色通道,就是不会被拦截器链处理的通道,自定义服务 IProvider 和 Fragment 就是走的绿色通道
      • 如果我们想让某个跳转操作跳过拦截器,可以在 navigation() 前调用 greenChannel() 方法
    • serializationService

      • 当我们调用 withObject() 方法时,ARouter 就会获取我们自己自定义的序列化服务 SerializationService,然后调用该服务的 object2Json() 方法,再把数据转化为 String 放入 bundle 中

二. ARouter 路由表生成原理

1. 注解处理流程

注解处理流程

  • 关于 Android Gradle 的 和 Javac 这里不会展开讲,在这里我们只需要理解在对 @Route 等注解处理前,是由 Android Gradle 的 AndroidJavaCompiler 来发起编译操作,然后让 Javac 去调用 RouteProcessor 等注解处理器对注解进行处理的

2. 路由解析流程

路由解析流程

  1. 获取路由元素

    • 这里的元素指的是 javax 包中的 Element ,Element 表示 Java 语言元素,比如字段、包、方法、类以及接口
    • process()方法会接收到 annotations 和 roundEnv 两个参数,annotations 就是当前处理器要处理的注解,roundEnv 就是运行环境 JavacRoundEnvironment
    • JavacRoundEnvironment 有一个 getElementsAnnotatedWith() 方法,RouteProcessor 在处理注解时首先会用这个方法获取元素
  2. 创建路由元信息

    创建路由元信息

    • 这里说的路由元信息指的是 RouteMeta,RouteProcessor 会把声明了 @Route 注解的的 Activity、Provider、Service 或 Fragment 和一个 RouteMeta 关联起来
    • 当元素类型为 Activity 时,RouteProcessor 会遍历获取 Activity 的子元素,也就是 Activity 的成员变量,把它们放入注入配置 RouteMeta 的 injectConfig 中,当我们配置了要生成路由文档时,RouteProcessor 就会把 injectConfig 写入到文档中,然后就对元信息进行分组
  3. 把路由元信息进行分组

    • 在 RouteProcessor 中有一个 groupMap,在 RouteMeta 创建好后,RouteProcessor 会把不同的 RouteMeta 进行分组,放入到 groupMap 中
    • 拿路径 /goods/details 来说,如果我们在 @Route 中没有设置 group 的值,那么 RouteProcessor 就会把 goods 作为 RouteMeta 的 group
  4. 生成路由表

    生成路由表

    • 当 RouteProcessor 把 RouteMeta 分组好后,就会用 JavaPoet 生成 Group、Provider 和 Root 路由文件,路由表就是由这些文件组成的,JavaPoet 是 Square 开源的代码生成框架

    • 生成路由文件后,物流中心 LogisticsCenter 需要用这些文件来填充仓库 Warehouse 中的 routes 和 providerIndex 等索引,然后在跳转时根据 routes 和索引来跳转

    • 关于 LogisticsCenter 和 Warehouse 在后面会讲,下面我们来看下路由文件的内容

    • RouteProcessor 生成的路由文件位于 build/generated/source/kapt/(debug/release)/com/alibaba/android/arouter/routes

    • 以 ARouter 示例项目中的一个路由文件为例,RouteProcessor 创建的 RouteMeta 会转化为下面这样的文件

      路由文件

      • 其他的路由文件也是大同小异的,所有内容都是由 RouteProcessor 填充的

三. ARouter 跳转原理

  • 当我们调用 Postcard 的 navigation() 方法时,Postcard 会调用 ARouter 的 navigation() 方法,然后 ARouter 才会去加载路由表,所以我们来看下 navigation() 的处理流程

  • ARouter 的 navigation() 方法有下面两种重载

    • navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback)
    • navigation(Class<? extends T> service)
  • 第一种重载是进行跳转的,第二种重载是用来创建服务的,下面我们来看第一种重载的实现

    ARouter.navigation()

1. 预处理服务

  • 实现预处理服务

    实现预处理服务

    • 预处理服务可以让我们在 ARouter 进行跳转前,根据 PostCard 的内容判断是否要独立地对这次跳转进行处理,是的话则在 onPretreatment() 中返回 false 即可
  • 预处理服务处理流程

    预处理服务处理流程

    • navigation() 中,首先会调用预处理服务的 onPretreamtn() 方法,判断是否要继续往下处理,如果返回结果为 false ,则不再往下处理,也就是不会进行跳转等操作

2. 完善明信片

完善明信片

  • 获取路由元信息

    获取路由元信息

    • 在 ARouter 初始化时,会把 LogisticsCenter 也进行初始化,而 LogisticsCenter 的初始化方法中,会读取 RouteProcessor 创建好的路由表,然后放到对应的索引 index 中
    • 有了索引,当 ARouter 调用 LogisticsCenter 的 completion() 方法时,就可以用索引从 Warehouse 的 routes 中获取路由元信息
    • 如果 LogisticsCenter 根据索引查找不到对应的 RouteMeta,那就说明 routes 还没有被填充,这时 LogisticsCenter 就会获取 group 的 RouteMeta,然后把 group 下的路径填充到 routes 中,然后再调用一次 completion() ,这时就可以取填充明信片的信息了
    • 索引是在 LogsticsCenter 初始化的时候加载的,后面在路由表加载原理的时候会讲到
  • 填充明信片信息

    • 我们在调用 ARouter.getInstance().build() 方法时,就是创建了一张 Postcard,但是这张 Postcard 的信息是不完整的
    • 比如 Postcard 中有一个 destination 字段,destination 表示跳转目标的 Class 信息,跳转 Activity 要用 Intent,destination 就是 Intent 的构造函数的第二个参数
    • 而 LogisticsCenter 就要负责把从路由表获取到的 destination 信息填充到这张明信片中,有了一张信息完整的明信片,才能进行后续的跳转操作
  • 初始化 Provider

    • 填充完了 Postcard 的信息后,LogisticsCenter 会根据 Postcard 的类型来做不同的操作,如果是 Provider 的话,就会调用 Provider 的初始化方法,并且把 Postcard 设为绿色通道
    • 如果是 Fragment 的话,那就只把 Postcard 设为绿色通道,如果是其他类型,则不设为绿色通道,这里说的绿色通道,其实就是说 Provider 和 Fragment 是跳过拦截器链的

3. 降级策略

  • 所谓的降级策略,其实就是跳转失败时,我们能够跳转到别的页面,比如一个跳转失败提示页
  • 假如 ARouter 在完善明信片信息的过程中遇到了异常
  1. 降级策略处理流程

    降级策略处理流程

    • 调用完预处理服务后,navigation() 就会通过物流中心 LogisticsCenter 来填充 PostCard 内容,如果在填充过程中遇到了异常,就会调用降级服务
  2. 自定义降级策略

    自定义降级策略

    • 在自定义降级策略时,要注意 context 可能会空,要使用 Context?

4. 拦截器

  • 拦截器可以用来在跳转过程中处理事件,比如做登陆检查,拦截器会在跳转之间执行,多个拦截器会按优先级顺序依次执行
  1. 实现拦截器

    实现拦截器

    • 实现拦截器时,我们要调用 onContinue()onInterrupt() 方法,至少需要调用其中一种方法,否则不会继续路由
    • onContinue():这个方法表示处理完成,交换控制权给 ARouter
    • onInterrupt():当我们觉得有问题时,可以用 onInterrupt() 方法,传一个异常,以中断路由流程
  2. 拦截器处理流程

    拦截器处理流程

    • navigation() 在完善 Postcard 信息后,就会判断该 Postcard 是否通过绿色通道来处理,绿色通道就是不经过拦截器链的通道
    • 在 LogisticsCenter 的 completion() 中,中会把路由类型为 Provider 和 Fragment 的路线设为绿色通道,如果我们想让目标页面跳过拦截器链,就可以在 navigation() 方法前调用 greenChannel() 方法

5. 按类型跳转

按类型跳转

  • 当处理完拦截器后,navigation() 中就会调用 navigation() 方法,这也是具体进行跳转的方法。
  • 在这个方法中,会根据 Postcard 的路由类型 RouteType 来判断怎么跳转
  1. Activity

    • 当 RouteType 为 Activity 时,启动 Activity 的流程和我们平时启动 Activity 的流程是一样的,创建 Intent、传入 destination、设置 fragment 和重写动画等,最终调用 startActivity() 启动目标页面
  2. Fragment/Broadcast/ContentProvider

    • 当跳转目标为 Fragment、Broadcast 或 ContentProvider 时,会通过 destination 用反射创建实例,如果是 Framgent ARouter 还会为它设置要传递给目标 Fragment 的参数,然后返回实例
  3. Provider

    • 如果跳转目标为 Provider,也就是自定义服务的话,就对应了后面讲 ARouter 自定义服务时讲的“通过依赖查找发现服务”

6. 跳转回调

跳转回调

  • 在调用 navigation() 跳转时,我们可以在 navigation() 中传入 NavigationCallback 监听跳转过程中发生的事件

    • onLost():无法查找到跳转目标

    • onFound():找到了跳转目标

    • onInterrupt():拦截器中断了跳转

    • onArrival():已打开跳转目标

      跳转

    • 除了 NavigationCallback ,我们也可以用 NavCallback 监听跳转中发生的事件,NavCallback 是 ARouter 中实现了 NavigationCallback 的一个抽象类,使用 NavCallback 不会强制要求我们重写所有方法,只要求重写 onArrival() 方法

      onArrival()

四. ARouter 路由表加载原理

  • 所谓的加载路由表,其实就是加载 RouteProcessor 生成的类文件。
  • 在我们调用 ARouter 的 init() 方法时,ARouter 会调用 LogisticsCenter 的 init() 方法,在 LogisticsCenter 的 init() 方法中,会判断当前路由表加载方式是否为插件,不是的话则从 Dex 中加载路由表,是的话则由插件从 Jar 中加载路由表

1. 从 Dex 中加载路由表

从 Dex 中加载路由表

  • 读取 Dex 文件

    • LogisticsCenter 发现没有用插件加载路由表时,就会用 ClassUtils 读取路由表。
    • 这里说的路由表,其实就是 RouteProcessor 生成好的类文件类名,而读取 Dex 文件的方式,就是从源码目录(applicationInfo.sourceDir) 中读取 base apk 的路径,然后用这个路径构建一个 DexFile(path)
  • 从 Dex 文件中读取路由表

    • ClassUtils 用 DexFile 读取 apk 中的类信息,然后判断类的包名是否为 “com.alibaba.android.arouter.routes” ,是的话说明这是 ARouter 的注解处理器生成的路由文件,把匹配上的类加入列表中,然后把列表返回给 LogisticsCenter
  • 把路由表保存到本地

    • 当 LogisticsCenter 从 ClassUtils 中获取到注解处理器生成的类名时,就会把这些类名保存 SharedPreferences 中,下次就根据 App 版本判断,如果不是新版本,就从本地中加载类名,否则就用 ClassUtils 读取类名
  • 把路由信息保存到索引中

    • 当 LogisticsCenter 把路由表保存到 SharedPreferences 后,就会根据类名的后缀判断类是 IRouteRoot 、IInterceptorGroup 还是 IProviderGroup ,然后根据不同的类把类文件的内容加载到索引中

2. 从 Jar 中加载路由表

  • 如果我们想缩短 ARouter 的初始化时间,可以用 ARouter 的 Gradle 插件,这个插件能自动加载路由表,这样 ARouter 初始化的时候就不需要读取类的信息,从而缩短初始化时间

    插件

  • Register 插件从 Jar 文件加载路由表的流程如下

    Jar

    • PluginLaunch.apply()

      • 当我们运行 App 时,Gradle 就会调用我们依赖的 ARouter Register 插件,这个插件的执行的起点就在 PluginLaunch.apply() 中,PluginLaunch 判断了只有在运行的项目为 Applicaiton 时,才会加载路由表信息
    • BaseExtention.registerTransform()

      • Android Gradle 插件包含了一个 Transform API,这个 API 允许第三方插件在编译后的类文件转换为 dex 文件前做处理操作,而 ARouter 的 Register 插件就实现了一个 RegisterTransform
    • RegisterTransform.transform()

      • 当 Gradle 的任务管理器 TaskManager 执行 TransformTask 时,就会执行 RegisterTransform 的 transform() 方法
      • 在 transform() 方法中,会接收到一个 TaskManager 传过来的 TransformInput 集合,通过 TransformInput 可以获取到 Gradle 为我们项目生成的 Jar 和 Class 文件,包括 RouteProcessor 生成的文件
    • ScanClassVisitor.visit()

      • 当 RegisterTransform 获取到了 Class 文件后,就会用 ScanUtils 中的 ScanClassVisitor 访问 Class 中的字节码数据,包括 Class 的包名和类名
      • ScanClassVisitor 是 ClassVisitor 的子类,ClassVisitor 可以改变修改字节码,当 ScanClassVisitor 发现当前 Class 实现了 IRouteRoot、IIntercetorGroup 或 IProviderGroup 接口时,这三个接口都是就会把当前 Class 的类名添加到 ScanSetting 的 classList 中
    • RegisterCodeGenerator.insertInitCodeIntoJarFile()

      • 当 RegisterTransform 把 ScanSetting 中的 classList 初始化完后,就会用代码生成器 RegisterCodeGenerator 插入代码到 Jar 文件中

      • 在 RegisterCodeGenerator 的 insertInitCodeIntoJarFile() 方法中,会把 Jar 文件转化为 JarFile 对象,然后获取到 JarFile 中的 JarEntry ,找到 LogisticsCenter,然后把路由表插入到 LogisticsCenter 的 loadRouteMap() 方法中,比如下面这样

        LogisticsCenter

      • Jar 文件路径:build/intermediates/transforms/com.alibaba.arouter/debug/41.jar

五. ARouter 进阶用法与注意事项

1. 传参

传参

2. 解析参数

解析参数

  • @JvmField

    • 这里需要注意的是,如果 Activity 或 Fragment 是用 Kotlin 写的,那字段就要加上 @JvmField ,以提供 getter()setter() 给 ARouter 使用
  • 默认值不能为空

    • 这里的 key 的值会被取做默认值,不能为空,否则会出现空指针异常
    • 因为 ARouter 在用 Intent 的 getLongExtra() 等方法获取参数时,这些方法默认值不是包装类型,如 Long ,而是基本类型,如 long
  • withObject()

    • 使用 withObject() 传递 List 和 Map 时,接收该对象的地方不能标注具体的实现类类型,应仅标注为 List 或 Map,否则会影响序列化中类型的判断, 其他类似情况需要同样处理
  • 查看异常

    • 如果你发现接收不到你想要的参数时,可以在 AutowiredServiceImpl 的 autowired() 方法中打个断点看下遇到了什么异常
  • 查看编译日志

    • 如果在使用 ARouter 的过程中,出现了编译失败但是不知道原因时,可以用命令查看详细的编译日志 gradle aseembleDebug --stacktrace

3. 实现序列化服务

  • ARouter 允许我们设置序列化服务,如果需要传递自定义对象,那只需要新建一个实现 SerializationService 的类,并加上 @Route注解
  • 这里的 yourservicegroupname ,指的是我们可以给服务设定不同的分组

实现序列化服务

4. 自定义服务

  • 声明接口

    声明接口

  • 实现接口

    实现接口

  • 通过依赖注入发现服务(推荐)

    通过依赖注入发现服务

    • helloService 和 helloService2 就是通过依赖注入的方式发现服务的,也就通过注解标注字段即可使用,无需主动获取。
      • byType:不设置 name 属性的话,会默认使用 byType 的方式发现服务,helloService 就是 byType。
      • byName:Autowired 注解中标注 name 之后,将会使用 byName 的方式注入对应的字段。当同一接口有多个实现的时候,必须使用byName的方式发现服务,helloService2 就是 byName
  • 通过依赖查找发现服务

    通过依赖查找发现服务

    • 主动发现服务并使用,helloService3 是 byName ,helloService4 是 byType

5. 通过 URL 跳转

  • 如果我们想让应用直接处理外部 URI,只要在清单文件中进行配置,再把 url 传递给 ARouter 即可

  • 设定 host:首先在 Manifest 中声明 Activity 的 action 和 category ,并且在 data 标签中添加主机和协议

    设定 host

  • 监听 Scheme:然后新建一个Activity用于监听Scheme事件,直接把 url 传递给 ARouter 即可

    监听 Scheme

6. 设置转场动画

设置转场动画

7. 获取 Fragment

获取 Fragment

  • 获取 Fragment 时要注意,当找不到对应路径的 Fragment 时,会返回 null,所以这里用空安全的 as?

六. 其他

1. ARouter 参数生成与加载

  • 没有讲参数的生成与加载流程,这个流程和 RouteProcessor 生成路由表的原理类似
  • 对于参数,ARouter 会用 AutowiredProcessor 来生成参数表,这个表的类实现了注射器 ISyringe 接口
  • 而我们调用的 ARouter.inject() 方法,就是在加载参数表的内容
-------------------- 本文结束感谢您的阅读 --------------------