Android TheRouter 笔记

TheRouter是货拉拉开源的一套专门面向模块化/组件化开发的一整套解决方案框架。它的设计理念比较超前,不仅解决了页面跳转的解耦问题,还顺带把模块化开发中常见的依赖注入、模块初始化、动态化配置等问题都给系统地解决了。

简单来说,TheRouter的核心功能可以概括为四大块,我们可以一个个来看:

📦 TheRouter的四大核心功能

  • Navigator(页面导航):这是路由最基础的能力,用于解耦模块间的页面跳转依赖。但它做得很细:

    • 多对一路由 :支持多个 path 指向同一个页面(Activity/Fragment)。这个特性对于双端(Android/iOS)路由表统一很有用,能降低多端对齐的成本。
    • 动态路由表:这是它的一个亮点。允许通过远端下发JSON配置来动态修改路由表。比如线上某个原生页面出了严重Bug,可以直接下发配置,将这个页面的跳转目标临时降级为一个H5页面,实现快速的"容灾"。
    • 参数传递 :除了Intent支持的基本类型,还可以通过 withObject() 传递任意对象,甚至支持在路由表中为页面预设默认参数。
    • 拦截器体系 :提供了 PathFixHandler(修复Path)、PathReplaceInterceptor(替换Path)、NavigatorInterceptor(路由拦截)等多层级的拦截机制,可以在跳转前/后插入各种逻辑,比如登录拦截。
  • ServiceProvider(服务依赖注入):这是为了解决模块间功能调用的问题。它参考了SOA(面向服务架构)的设计思想,让模块之间只依赖接口,而不依赖具体的实现类。

    • 接口隔离 :比如订单模块需要获取用户信息,它只需要依赖一个 IUserService 接口,而不用关心这个接口是哪个模块实现的。
    • 服务注册与发现 :服务提供方通过 @ServiceProvider 注解标记一个静态方法或类,将服务实现"注册"到框架中。服务使用方则通过 TheRouter.get(IUserService::class.java) 来"发现"并使用这个服务。
    • 灵活的对象管理 :你可以通过 @Singleton@NewInstance 等注解来控制服务对象的创建模式(单例、每次都新建或默认的LRU缓存),这在性能和状态管理上给了开发者很大的自由度。
  • FlowTaskExecutor(单模块自动初始化):在模块化项目里,每个模块可能都需要在Application启动时做一些初始化工作。传统做法是在Application里手动调用各个模块的初始化方法,耦合度高且难以维护。

    • 任务化 :TheRouter把这个过程抽象成了一个个的 FlowTask。你只需要在任意类的方法上加上 @FlowTask 注解,并声明它的任务名和依赖关系(比如 dependsOn = TheRouterFlowTask.APP_ONCREATE),框架就会在恰当的时机(例如Application的onCreate()之后)自动、按依赖顺序初始化这些模块。
    • 循环依赖检测:它甚至在编译期就能检测出任务之间的循环依赖,避免运行时死锁。
  • ActionManager(动态化能力):这是一个全局的、类似事件总线的机制,但又比普通的观察者模式更强大。

    • 链式响应与优先级 :你可以定义某个 Action(通过一个URI标识),并指定它的响应者。当这个Action被触发时,多个响应者可以按照优先级链式处理。
    • 可追踪性:它能记录整个调用路径,这对于调试那些"不知道谁调用了谁"的复杂业务逻辑来说,简直是神器。

⚙️ 实现原理深度剖析

TheRouter之所以功能强大且性能优异,关键在于它巧妙地在编译期做了大量工作,最大程度地避免了运行时的性能损耗。

  • 编译期:代码生成与聚合

    1. 注解处理 :在编译时,通过APT(注解处理工具)或更高效的KSP(Kotlin Symbol Processing),扫描所有模块中被 @Route@ServiceProvider@FlowTask 等注解标记的代码。
    2. 生成中间代码 :针对每个模块,它会生成一个 RouteMap__ 开头的Java类。这个类里面包含了两个核心部分:
      • 一个静态常量字符串 ROUTERMAP,记录了该模块的路由信息(如pathclassNameparams)的JSON格式。
      • 一个静态方法 addRoute(),里面是一系列直接调用框架API的代码,用于将该模块的路由项添加到全局路由表中。
    3. Gradle插件聚合 :最后,通过自定义的Gradle插件,它会遍历所有依赖的模块(包括AAR和源码),找到所有 RouteMap__ 类,并将它们的 addRoute() 方法统一收集到一个名为 TheRouterServiceProvideInjecter 的类中。
  • 运行时:无反射加载与高效跳转

    1. 无反射初始化 :在应用启动调用 TheRouter.init() 时,框架内部会直接调用 TheRouterServiceProvideInjecter 类中的方法。由于这些方法是编译期生成的直接调用,整个过程完全没有使用反射,因此初始化速度非常快。
    2. 路由表存储 :所有路由信息会被加载到一个支持正则表达式匹配的 Map 结构中。这也是它能实现 path 一对多的技术基础。
    3. 跳转流程 :当调用 TheRouter.build(path).navigation() 时,整个过程大致如下:
      • 创建Navigatorbuild() 方法会创建一个 Navigator 对象。在构造器中,会先执行开发者设置的 PathFixHandler,对原始的 path 进行修正。
      • 解析参数 :接着,解析 path 中的query参数(如 ?key=value),并将其存储到 Navigator 内部的 Bundle 中。
      • 查找路由项 :当调用 navigation() 时,框架会用最终的 path 去那个支持正则的 Map 中查找匹配的 RouteItem
      • 执行拦截器与跳转 :如果找到了目标页面,会依次执行 PathReplaceInterceptorNavigatorInterceptor,最后通过 startActivity()startActivityForResult() 完成最终的跳转。

🆚 横向对比

TheRouter在设计之初就对标了业内主流的ARouter和WMRouter,解决了很多痛点。这里有一个简单的对比:

  • 性能 :TheRouter在编译期 通过Gradle插件聚合代码,实现了无运行时扫描、无反射的加载方式,性能损耗最小。相比之下,ARouter在运行时需扫描Dex并反射实例化类,WMRouter也需运行时读文件和反射。
  • 动态性 :TheRouter原生支持远端路由表下发,可用于页面降级容灾,这是它的一大特色。其他两者则不具备或支持较弱。
  • 功能完整性 :除了基础路由,TheRouter还内置了服务注入、模块自动初始化、ActionManager等一整套模块化解决方案。其他框架则主要集中在路由本身。
  • 对热修复友好:由于采用了独特的代码生成策略,对于未改动路由表的模块,多次编译生成的中间代码不会有变化,因此不会产生无意义的热修复补丁体积。
  • 易用性 :支持KSP编译 ,编译速度比kapt更快。同时支持多Path对应同一页面,方便双端路由统一。

TheRouter是一套为企业级模块化开发 量身打造的解决方案。它通过在编译期下功夫换来了运行时的高性能 ,并通过丰富的功能集,将模块化过程中的页面跳转、服务调用、模块初始化、动态配置 等问题统一了起来。特别是它的动态路由下发能力,为应用的线上容灾提供了很实用的手段。

PathFixHandler 是 TheRouter 中一个非常实用的轻量级拦截点,用于在路由构建的最早期对原始 path 进行统一修正。它属于框架中"路径修复 "这一层,比 PathReplaceInterceptor 执行得更早,并且专注于格式规范化默认值补全。下面我们来详细拆解它的实现原理和替换机制。


1. PathFixHandler 的定义与作用

PathFixHandler 是一个函数式接口(通常只有一个方法),定义大致如下:

java 复制代码
public interface PathFixHandler {
    String fix(String originalPath);
}

它的作用是接收原始 path 字符串,返回一个修复后的新 path 字符串。常见的使用场景包括:

  • 补全 Scheme :例如将 "home" 自动补全为 "therouter://home"
  • 统一大小写/格式 :将 "user/profile" 统一为小写 "user/profile"
  • 旧版路径兼容 :将旧版本的 "old/home" 映射为 "home"
  • 环境切换 :根据 BuildConfig 动态添加环境前缀(如 "dev/""prod/")。

与拦截器的区别在于:PathFixHandler 发生在路由构建的最开始,仅对字符串做变换,不涉及路由表查找或拦截决策。


2. PathFixHandler 的注册方式

TheRouter 允许开发者通过全局 API 添加多个 PathFixHandler,通常是在应用初始化时进行:

java 复制代码
// 添加一个补全 Scheme 的 Handler
TheRouter.addPathFixHandler(path -> {
    if (!path.contains("://")) {
        return "therouter://" + path;
    }
    return path;
});

// 可以继续添加其他 Handler
TheRouter.addPathFixHandler(path -> path.toLowerCase(Locale.US));

这些 Handler 会被存储在一个 List<PathFixHandler> 中,按照添加顺序排列。


3. 调用链机制:链式顺序执行

当调用 TheRouter.build(path) 时,会创建一个 Navigator 对象。在 Navigator 的构造器中,会立即对所有已注册的 PathFixHandler 进行链式调用

java 复制代码
public Navigator(String path) {
    // 1. 获取全局的 PathFixHandler 列表
    List<PathFixHandler> fixHandlers = TheRouter.getPathFixHandlers();
    
    // 2. 依次应用修复
    String fixedPath = path;
    for (PathFixHandler handler : fixHandlers) {
        fixedPath = handler.fix(fixedPath);
    }
    
    // 3. 将修复后的 path 保存到 Navigator 内部
    this.path = fixedPath;
    
    // 4. 解析 query 参数等后续操作...
}

这意味着:

  • 每个 Handler 都会接收到上一个 Handler 处理后的结果。
  • 最终的 path 将作为后续路由查找和跳转的依据。
  • 如果某个 Handler 返回 null,则会中断后续处理并导致 build() 失败(通常框架会使用原始 path 或者抛出异常,具体取决于实现)。

这种设计保证了修复逻辑的可组合性顺序性


4. 替换逻辑的深入分析

"替换"的本质就是字符串变换。开发者可以在 fix() 方法中实现任意复杂的逻辑,例如:

  • 正则替换:将匹配特定模式的部分替换为其他内容。
  • 查表映射:根据一个预定义的 Map 进行路径映射。
  • 动态下发配置:从远端获取的配置中查找替换规则。

由于 PathFixHandler 在 build() 阶段执行,此时尚未解析参数,因此无法直接访问 Navigator 中的参数 Bundle,但可以通过全局状态(如 SharedPreferences)或依赖注入获取配置。

示例:根据远端配置进行路径替换

假设我们有一个远端下发的 JSON,定义了某些旧路径需要重定向到新路径:

json 复制代码
{
  "path_redirect": {
    "/old/user": "/user/profile",
    "/old/order": "/order/list"
  }
}

可以在 PathFixHandler 中加载这个配置:

java 复制代码
TheRouter.addPathFixHandler(path -> {
    Map<String, String> redirectMap = ConfigManager.getPathRedirectMap();
    String target = redirectMap.get(path);
    return target != null ? target : path;
});

这样所有符合配置的旧路径都会被自动替换为新路径。


5. PathFixHandler 与 PathReplaceInterceptor 的对比

为了更清晰地理解职责划分,这里做一个对比:

特性 PathFixHandler PathReplaceInterceptor
执行时机 build() 构造器中,查找路由表之前 navigation() 中,查找路由表之前
输入输出 输入原始 path,输出修复后的 path 输入原路由项和目标 path,输出新的路由项或 path
典型用途 格式规范化、Scheme补全、默认值 模块化解耦、根据业务状态动态替换目标页面
能否访问路由表 不能 可以(因为已加载路由表)
是否影响参数 不影响(仅 path 字符串) 可以修改参数(通过替换路由项)

简单来说,PathFixHandler 更偏向于字符串预处理 ,而 PathReplaceInterceptor 更偏向于路由项的动态替换


6. 源码级实现要点

在 TheRouter 的源码(以 v1.x 为例)中,TheRouter.java 内部维护了一个 CopyOnWriteArrayList<PathFixHandler>,保证线程安全。addPathFixHandler 方法将 handler 添加到此列表。build(String path) 方法内部会调用 applyPathFix(path) 私有方法,完成上述链式处理。

关键代码片段(伪代码):

java 复制代码
public class TheRouter {
    private static final List<PathFixHandler> FIX_HANDLERS = new CopyOnWriteArrayList<>();

    public static void addPathFixHandler(PathFixHandler handler) {
        FIX_HANDLERS.add(handler);
    }

    private static String applyPathFix(String path) {
        String result = path;
        for (PathFixHandler handler : FIX_HANDLERS) {
            result = handler.fix(result);
            if (result == null) {
                // 处理异常,可回退或抛出
                break;
            }
        }
        return result;
    }

    public static Navigator build(String path) {
        String fixedPath = applyPathFix(path);
        return new Navigator(fixedPath);
    }
}

7. 注意事项与最佳实践

  • 不要执行耗时操作 :PathFixHandler 在主线程的 build() 中执行,应避免网络请求或复杂计算,否则会影响跳转性能。
  • 保持幂等性:多次应用同一个 handler 不应产生副作用,因为 handler 可能会被重复调用(如多次 build)。
  • 顺序依赖:如果多个 handler 之间有依赖关系,需谨慎安排添加顺序。
  • 避免死循环 :如果 handler 中又调用了 TheRouter.build(),可能会触发无限递归,需要避免。
  • 日志与调试 :可以借助 TheRouter.setDebuggable(true) 开启日志,观察 path 的修复过程。

8. 总结

PathFixHandler 是 TheRouter 提供的一个轻量级的、可插拔的路径预处理机制。它通过在 build() 阶段对原始 path 进行链式替换,实现了统一格式化、默认值补全、动态映射等能力,且对开发者透明。这种设计体现了"关注点分离"的原则,让路由的核心逻辑保持简洁,同时赋予了框架极高的灵活性和扩展性。

在实际项目中,合理利用 PathFixHandler 可以极大地简化路由管理,尤其是对于大型多模块应用,它能帮助团队统一规范、兼容旧版路径,甚至实现动态降级。

相关推荐
城东米粉儿7 小时前
Android AIDL 笔记
android
城东米粉儿7 小时前
Android 进程间传递大数据 笔记
android
城东米粉儿8 小时前
Android KMP 笔记
android
冬奇Lab9 小时前
WMS核心机制:窗口管理与层级控制深度解析
android·源码阅读
松仔log10 小时前
JetPack——Paging
android·rxjava
城东米粉儿10 小时前
Android Kotlin DSL 笔记
android
城东米粉儿10 小时前
Android Gradle 笔记
android
城东米粉儿10 小时前
Android Monkey 笔记
android