Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

组件化路由与模块通信

组件化不是把包拆成很多 module,而是让业务边界、依赖方向和发布协作更清晰。中高级面试常追问路由、通信和依赖治理。

一、组件化解决什么问题

问题组件化目标
业务耦合feature 边界清晰
编译慢模块增量构建
多团队协作独立开发/测试
依赖混乱依赖方向可控

二、模块拆分方式

  • app 壳工程:组装入口。
  • feature module:业务功能。
  • core/common:基础能力。
  • api/contract:跨模块接口。

依赖方向应从上层依赖抽象,避免 feature 之间直接互相依赖。

三、路由框架原理(以 ARouter 类框架理解)

核心流程:编译期扫描注解 → APT/KSP 生成路由表 → 运行时按 path 查找目标 → 拦截器处理登录/权限/降级 → 跳转或返回服务实例。

interface UserService {
    fun currentUserId(): String?
}

跨模块不要直接调用实现类,而是依赖 UserService 这样的 contract。

四、模块通信方式

方式适用风险
路由跳转页面级跳转path 字符串错误
接口下沉同步能力调用contract 膨胀
事件总线广播式通知难追踪、生命周期问题
Result API页面结果回传复杂链路难维护

五、路由拦截、降级与灰度

登录校验、权限校验、A/B 实验、页面不存在降级都适合放在拦截器链里。路由失败必须有兜底,不能直接 crash。

六、和 Gradle 工程化的关系

组件化设计关注业务边界;Gradle 工程化关注构建配置、依赖版本、产物和性能。两者相关但不是一回事。

七、现代组件化治理:契约、构建逻辑与类型安全路由

面试官往往不满足于“用了 ARouter”,会继续追问模块怎么拆、依赖怎么防腐、路由怎么治理。

依赖方向

app
 ├─ feature-home-impl  ──> feature-home-api
 ├─ feature-pay-impl   ──> feature-pay-api
 └─ core-ui / core-network / core-common
  • api/contract 模块只放接口、路由契约、DTO,不放具体页面和重业务逻辑。
  • impl 模块实现页面和业务,依赖自己的 api 与公共 core。
  • feature 之间不直接依赖实现,避免循环依赖和“改 A 编译半个项目”。

Convention Plugin 与 Version Catalog

组件化后如果每个 module 都复制 Android/Kotlin/Compose 配置,会迅速失控。更好的做法是把公共配置沉淀到 Gradle convention plugin,并用 Version Catalog 管理依赖版本。

治理对象推荐方式面试表达
compileSdk/minSdk/Kotlin 版本convention plugin全项目一致,减少漂移
依赖版本version catalog可审计、可统一升级
lint/ktlint/测试配置convention plugin新模块默认接入质量门禁
feature 开关配置中心/构建变体支持灰度和降级

类型安全路由契约

字符串 path 容易写错、参数缺失难发现。可以在 contract 层定义类型安全入口:

data class PayRoute(val orderId: String, val source: String)

interface PayNavigator {
    fun openPay(route: PayRoute)
}

底层可以仍然用路由框架,但上层业务依赖的是类型化 contract,而不是到处拼 "/pay/detail?orderId=..."

Dynamic Feature / 插件化边界

动态交付适合低频、体积大的功能,但会带来下载失败、版本兼容、冷启动和路由降级问题。面试中要强调:动态化不是为了炫技,而是为了控制包体积、灰度风险或业务隔离。

路由治理清单

  1. path 命名规范和归属人。
  2. 参数 schema 与必填校验。
  3. 未命中、版本不兼容、未安装动态模块时的降级页。
  4. 登录/权限/风控拦截器顺序。
  5. 路由耗时、失败率、来源页面埋点。

高频面试题

Q1:组件化和模块化区别? 答:模块化偏代码物理拆分,组件化更强调业务组件独立开发、独立测试、按契约通信和可组装。

Q2:ARouter 这类框架大概怎么实现? 答:编译期注解处理生成路由表,运行时初始化加载,按 path 找目标 class/provider,再通过拦截器处理统一逻辑。

Q3:跨模块通信为什么不直接依赖实现? 答:直接依赖会导致 feature 耦合、编译依赖变重、循环依赖风险;接口下沉能保持依赖方向稳定。

Q4:组件化项目怎么避免 Gradle 配置复制和依赖版本失控? 答:把公共 Android/Kotlin/Compose/lint/test 配置沉淀为 convention plugin,新模块默认套用;依赖版本放 Version Catalog 或统一依赖管理。这样既减少重复,也能在升级 compileSdk、Kotlin、AGP 时集中治理。

易错点 / 追问

  • 不要为了组件化制造过多小 module。
  • 不要把所有通信都塞进 EventBus。
  • 路由 path 要集中治理,否则重构时容易断链。