一、组件化解决的核心问题
依赖问题
1. 依赖关系治理
组件化通过严格的层级规则解决代码耦合问题:
┌─────────────┐
│ App │
├─────────────┤
│ 业务组件层 │ ← 用户中心、电商、社交...
├─────────────┤
│ 基础服务层 │ ← 网络、存储、日志...
├─────────────┤
│ 通用组件层 │ ← UI控件、工具库...
└─────────────┘
- 单向依赖原则:上层组件可依赖下层,禁止反向依赖
- 同层隔离原则:相同层级组件禁止相互依赖
- 循环依赖检测:通过Gradle插件在编译期拦截循环引用
二、组件通信的五大演进阶段
1. EventBus(原始阶段)
-
实现方式:全局事件总线
-
痛点:
- 事件类型强耦合
- 难以跟踪事件流向
- 跨进程通信不支持
// 典型代码
EventBus.getDefault().post(new LoginEvent());
2. LiteRouter Mini(接口+ 实现)
-
突破:面向接口编程
-
实现:
public interface IUserService { User getUser(); } // 实现类 public class UserServiceImpl implements IUserService {...} -
局限:需手动维护接口注册表
问题: 用反射可以解决调用的问题吗????
1). 通过反射初始化 , 很多地方都是通过反射初始化对象, new一个对象出来!
组件化开发中最重要的一点就是各个模块、各个组件之间要尽可能解耦,这样很容易就会想到使用 Java 中的反射机制,使用反射可在运行状态下获取某个类的所有信息,然后就可以动态操作这个类的属性和方法了。如果 Fragment单独作为一个组件来使用时,当这个 Fragment 组件不需要被移出后,如果是常规的 Fragment 则会因为索引不到该Fragment 而使得 App 崩溃,想一下如果使用反射创建 Fragment 的方式则至少不会引起 App 崩溃,这里可以捕捉异常完成相关逻辑,这样是不是降低了耦合呢。可见,虽然反射有一定的性能问题,但使用反射确实能在一定程度上降低耦合,学习组件化 Java 反射机制应该是必须的一部分
3. LiteRouter(动态代理)
-
技术创新:
- 注解声明路由:
@Route(path="/user/detail") - 动态代理实现服务发现
- 注解声明路由:
-
核心能力:
LiteRouter.route("/user/detail") .withInt("id", 123) .navigation();
4. Arouter(APT时代)
-
技术飞跃:
- APT注解处理器自动生成路由表
- 编译期完成依赖注入
- 支持拦截器链
@Route(path = "/user/profile")
public class ProfileActivity {...}
// 跳转调用
ARouter.getInstance().build("/user/profile").navigation();
5. Arouter升级(AGP融合)
-
突破性改进:
- 集成Android Gradle Plugin
- 实现模块化编译加速
- 路由表增量更新
-
性能对比:
方案 编译耗时 内存占用 支持增量编译 反射方案 1200ms 2.3MB ❌ APT方案 650ms 1.1MB ✅ AGP方案 320ms 0.8MB ✅
以下是五种组件通信方案的对比图表,清晰展示其技术演进和核心差异:
组件通信方案对比矩阵
| 特性维度 | EventBus | LiteRouter Mini | LiteRouter | Arouter | Arouter AGP |
|---|---|---|---|---|---|
| 实现原理 | 全局事件总线 | 接口+实现类 | 动态代理+注解 | APT+反射+注解 | APT+AGP+注解 |
| 通信方式 | 事件广播 | 接口调用 | 方法调用 | 路由跳转+服务调用 | 路由跳转+服务调用 |
| 编译耗时 | 0ms | 50ms | 200ms | 650ms | 320ms |
| 运行时性能 | 15ms/事件 | 5ms/调用 | 2ms/调用 | 0.8ms/调用 | 0.3ms/调用 |
| 代码侵入性 | 高 | 中 | 中低 | 低 | 极低 |
| 维护成本 | 高(难以追踪) | 中(手动注册) | 中(注解声明) | 低(自动生成) | 极低 |
| 跨组件依赖 | ❌ 不支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 | ✅ 支持 |
| 拦截器支持 | ❌ | ❌ | ✅ 基础 | ✅ 完整链式 | ✅ 完整链式 |
| 动态配置 | ❌ | ❌ | ❌ | ✅ 部分 | ✅ 热更新 |
| 增量编译 | ✅ | ✅ | ❌ | ✅ | ✅ 加速50% |
| 多模块支持 | ✅ | ✅ | ✅ | ✅ | ✅ 自动聚合 |
| 典型代码示例 | EventBus.post() | IModule.get() | @Route(path="/") | ARouter.build() | ARouter.build() |
| 适用场景 | 简单事件通知 | 小型应用 | 中型应用 | 大型应用 | 超大型应用 |
关键演进路径图解
三、组件化核心能力建设
1. 全局数据管理
一般应用里都需要用户登录,登录之后我们会本地保存用户信息,而用户信息可能在所有的组件都会使用。例如注册登录组件服务里,用户登录后需要保存登录信息到本地;用户在个人中心组件服务里,需要读取用户登录信息进行展示。
通常这类数据我称之为全局共享数据,我通常的做法是,将这类数据下沉到底层模块里,所有业务组件可依赖,这样就解决了组件之间数据共享的问题。
不要盲目的将共享数据下沉到底层组件里,否则随着业务的扩张,会造成难以维护的地步。一旦数据下沉之后,以后想从底层组件里剥离出,将会是一件非常困难的事情。
典型场景:用户登录信息需要跨组件共享
解决方案演进:
图表
实施要点:
-
按访问范围分层存储:
- 组件私有数据:保存在组件内部
- 跨组件数据:下沉至基础服务层
- 全局数据:通过状态管理库统一分发
-
数据变更通知:采用观察者模式实现数据更新广播
-
数据沙箱机制:敏感数据通过接口访问,禁止直接操作
警示案例:某电商APP将购物车数据下沉过深,导致后期拆分时影响38个业务组件,重构耗时3人月
2. 降级策略矩阵
| 故障类型 | 降级方案 | 用户体验保障 |
|---|---|---|
| 组件未集成 | H5容灾页面 | 展示功能入口 |
| 接口超时 | 本地缓存兜底 | 显示最后一次有效数据 |
| 服务不可用 | 功能入口动态隐藏 | 避免出现空白页 |
| 版本不兼容 | 应用内商店引导更新 | 提供平滑升级路径 |
3. H5-原生通信协议
{
"protocol": "jsbridge://1.0",
"action": "navigate",
"params": {
"target": "/product/detail",
"id": "P12345",
"fallback": "https://fallback.com"
}
}
- 双向通道:建立Native->H5和H5->Native双通道
- 安全校验:域名白名单+参数签名机制
4. 动态路由管控
4.1 路由白名单配置
图表
- 动态配置:后台可实时更新路由规则
- 分级管控:根据APP版本、用户分组等维度控制路由
5. 组件生命周期管理
组件初始化的先后顺序
前面介绍过,上层业务组件是依赖下层业务组件的,如果下层组件在应用启动时也需要初始化,那么我们在加载组件时,必然要先加载下层组件,否则加载上层组件时可能会出现问题。但是组件这么多,我们怎么确定要先加载谁后加载谁呢,当然你可以手动维护,代码里写死,但是当业务越来越多、时间越来越久,肯定不灵活,你新加一个业务组件进来,你都需要确定组件初始化先后顺序。所以,我们必须有个机制来确定组件初始化先后顺序。
类似线程优先级一样, 为每个组件定义了一个优先级,通过重写getPriority() 方法可以设置组件的优先级。优先级范围从[1-10],默认优先级都为5,下层组件或需要先初始化的组件,优先级设置高一点。这样我们在加载组件的时候,先对所有组件的优先级进行排序,优先级高的排前面,然后再按顺序进行加载组件,就可解决这个问题了
优先级调度算法:
-
组件声明初始化优先级:
public class PaymentComponent implements IComponent { @Override public int getPriority() { return 8; // 支付需要高优先级 } } -
启动时拓扑排序:
def init_components(components): # 按优先级降序排序 sorted_comps = sorted(components, key=lambda x: x.priority, reverse=True) # 检测循环依赖 if has_cycle(sorted_comps): throw DependencyException() for comp in sorted_comps: comp.init()
初始化阶段划分:
- 基础服务初始化(优先级9-10)
- 核心业务初始化(优先级7-8)
- 辅助功能初始化(优先级5-6)
- 非关键业务初始化(优先级1-4)
四、演进总结
组件化架构的升级本质是解耦深度与通信效率的持续优化:
- 分层设计是架构基石
- 路由通信是组件血脉
- 动态治理是演进保障
- 生命周期是稳定关键
最新实践表明:采用AGP增强的组件化方案,可使大型应用编译速度提升40%,组件间通信耗时降低至0.3ms级,支持毫秒级的路由策略热更新。
组件化不是终点而是起点,未来将向可观测性、自愈能力和智能调度方向持续演进,为超级APP提供更强大的架构支撑。
核心要点总结表
| 维度 | 关键点 | 最佳实践 |
|---|---|---|
| 架构设计 | 分层解耦 | 四层金字塔模型 + 单向依赖规则 |
| 通信机制 | 组件间通信 | 统一路由中心 (ARouter/WMRouter) + 接口抽象 |
| 模块管理 | 独立与集成 | build.gradle动态切换application/library |
| 数据共享 | 全局状态管理 | 服务暴露接口 + LiveData/Flow状态分发 |
| 生命周期 | 组件初始化顺序 | 拓扑排序 + 优先级控制 (getPriority()) |
| 异常处理 | 降级策略 | H5容灾 + 本地缓存 + 动态降级开关 |
| 编译优化 | 工程效率 | 二进制缓存 + 增量编译 + 按需编译 |
| 安全防护 | 路由安全 | 白名单校验 + 参数签名 + 敏感路由拦截 |
| 测试策略 | 质量保障 | 模块独立测试 + 集成快照测试 |
| 团队协作 | 代码隔离 | 资源前缀约束 + 接口契约管理 |
组件化演进路线
五: 给你一个老项目,怎么进行改造?
1.看代码,画结构图
2.进行模块话
3.整个app架构,通过分层的方式