Skip to content

Latest commit

 

History

History
113 lines (86 loc) · 7.26 KB

File metadata and controls

113 lines (86 loc) · 7.26 KB

好的,我们来深入剖析一下 ARouter 的实现原理。

ARouter 是阿里巴巴开源的一款用于帮助 Android App 进行组件化改造的框架,主要解决的是页面跳转、服务调用等模块间的依赖问题。其核心原理可以概括为:在编译期生成映射表,在运行期通过查找映射表来实现路由的分发。


核心思想:解耦

在没有 ARouter 之前,我们通常这样跳转页面:

Intent intent = new Intent(this, TargetActivity.class);
intent.putExtra("key", "value");
startActivity(intent);

这种方式导致 发起跳转的模块 必须依赖 目标模块,因为需要引用 TargetActivity.class。这在组件化架构中是不可接受的。

ARouter 的目标就是消除这种直接依赖,让跳转方只依赖一个“路径”(URL),而不需要知道目标类的具体存在。


原理详解:分阶段解析

ARouter 的实现可以分为两个关键阶段:编译期处理 和 运行期处理。

阶段一:编译期 - 注解处理与映射表生成

这是 ARouter 最核心、最巧妙的部分。

  1. 定义注解: ARouter 定义了 @Route 注解,开发者用它来标注哪些 Activity、Service 等是可以被路由的。

    @Route(path = "/app/main")
    public class MainActivity extends Activity { ... }
  2. 注解处理器(APT): ARouter 提供了一个注解处理器 arouter-compiler。在项目编译时,APT 会扫描所有带有 @Route 注解的类。

  3. 生成映射表文件: APT 会为每个使用了 @Route 注解的模块,在编译期生成一个特定的 Java 类。这个类的作用是保存该模块内所有的路由信息(路径 Path 和 目标类 Class 的映射关系)。

    例如,它可能会生成一个名为 ARouter$$Root$$app 的类,实现了 IRouteRoot 接口:

    public class ARouter$$Root$$app implements IRouteRoot {
        @Override
        public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
            routes.put("app", ARouter$$Group$$app.class);
            // 可能还有其他组...
        }
    }

    同时,它会生成更细粒度的 Group 类,如 ARouter$$Group$$app,这里面才包含了具体的 Path 到 Class 的映射:

    public class ARouter$$Group$$app implements IRouteGroup {
        @Override
        public void loadInto(Map<String, RouteMeta> atlas) {
            atlas.put("/app/main", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/app/main", "app", ...));
        }
    }

    为什么分 Root 和 Group? 这是一种优化策略。App 中的路由路径可能非常多,一次性全部加载到内存中会占用较大空间且耗时。通过分组(通常按模块名或功能划分),可以实现按需加载。只有当第一次访问某个分组下的路径时,才会加载该分组对应的映射表。

阶段二:运行期 - 初始化与路由寻址

  1. 初始化(“注册”): 我们在 Application 中调用 ARouter.init(...)。在这个阶段,ARouter 会做一件非常重要的事情:收集所有在编译期生成的映射表类。

    • 如何收集? 它利用了 DexFile 来扫描 APK 中所有的 dex 文件,查找特定包名(如 com.alibaba.android.arouter.routes)下的类。或者,在较新的版本中,它使用了 Google 推荐的 RouterTransform 来更高效地收集这些类名。
    • 建立索引:找到所有实现了 IRouteRoot 接口的类(即 ARouter$$Root$$xxx)后,通过反射实例化它们,并调用其 loadInto 方法。这样,ARouter 就在内存中建立了一个 “分组-映射类” 的索引关系(一个 Map),但此时还没有加载具体的 Path-Class 映射。
  2. 发起路由请求:

    ARouter.getInstance().build("/app/main").navigation();
  3. 路由寻址过程:

    • 解析路径:ARouter 首先解析你传入的路径 /app/main。它通常将第一级(如 app)作为分组名。
    • 检查分组是否已加载:ARouter 检查内存中是否已经加载了 app 这个分组的详细映射表(即 ARouter$$Group$$app 中的数据)。
      • 如果未加载,则从之前建立的 Root 索引中找到 ARouter$$Group$$app 这个类,通过反射实例化并调用其 loadInto 方法,将 /app/main -> MainActivity.class 这样的映射关系加载到另一个内存 Map(分组路由表)中。
    • 查找目标:从已加载的分组路由表中,根据完整路径 /app/main 找到对应的 RouteMeta 对象,这个对象里包含了目标类 MainActivity.class 以及路由类型(Activity、Fragment等)。
    • 拦截器处理(可选):在真正跳转之前,ARouter 会执行所有配置的拦截器(IInterceptor),形成一个责任链。这常用于登录验证、埋点等全局处理。如果某个拦截器中断了流程,则跳转终止。
    • 上下文转换:ARouter 拿到 MainActivity.class 后,在底层依然是通过构造一个 Intent,并调用 startActivity 来完成的。它帮你完成了从 路径字符串 到 目标 Class 的转换。
    // ARouter 内部大致会做这样的事
    Intent intent = new Intent(context, MainActivity.class);
    context.startActivity(intent);

关键技术点总结

关键技术 作用 说明
APT(注解处理器) 编译期生成代码 核心中的核心,生成路由映射表类,实现“注册”的自动化。
反射 运行期加载映射表 用于在初始化时加载 Root 和 Group 类。性能损耗主要在初始化时。
分组加载 性能优化 避免一次性加载所有路由映射,减少内存占用和初始化时间。
Dex文件扫描 收集映射表类 在初始化阶段找到所有编译期生成的类。
责任链模式 拦截器 提供可插拔的全局控制点,如登录检查。
构建者模式 API设计 ARouter.getInstance().build(path).navigation() 链式调用,清晰灵活。
SPI(服务发现) 服务暴露与发现 通过 @Route 注解暴露服务实现类,通过接口类型查找。原理与页面路由类似。

与其它方案对比

  • 原生 Intent:强依赖,无法实现组件化。
  • 隐式 Intent:通过 action 和 category 跳转。需要在 AndroidManifest.xml 中配置,管理繁琐,类型不安全(不能直接传递对象,需要序列化),跳转过程不可控。
  • 其他路由框架:原理大同小异,主要区别在于注解处理器的实现、映射表的结构与加载策略、以及性能优化细节。

结论

ARouter 通过 “编译期注解处理 + 运行期反射查找” 的经典组合,巧妙地解决了 Android 组件化中的页面和服务依赖问题。它将原本在 AndroidManifest.xml 中和代码中的硬编码依赖,转变为了基于字符串路径的弱依赖,并通过自动化的代码生成技术,让开发者几乎无感知地完成了“注册”工作,是 Android 组件化方案中非常关键和优秀的基础设施。