在 Android 组件化开发中,跨组件通信的核心目标是实现模块间解耦,同时保证高效和安全的数据传递。以下是常见的跨组件通信方案及其适用场景:
一、主流跨组件通信方式
1. 路由框架(如 ARouter、WMRouter)
-
原理:通过路由表映射 URI 到目标页面或服务,支持参数传递和拦截器。
-
特点:
- 解耦性强:组件间无直接依赖,通过 URI 跳转。
- 支持复杂场景:跨模块跳转、动态路由、降级策略(如跳转 H5 或默认页)。
- 扩展性高:可添加拦截器处理登录验证、埋点等逻辑。
-
代码示例:
kotlin// 声明路由 @Route(path = "/user/detail") class UserDetailActivity : AppCompatActivity() // 跳转调用 ARouter.getInstance().build("/user/detail") .withString("userId", "123") .navigation()
2. 事件总线(如 EventBus、LiveDataBus、RxBus)
-
原理:基于发布-订阅模式,组件通过事件对象通信。
-
特点:
- 轻量级:适合简单数据传递(如通知状态变化)。
- 强耦合风险:事件类型需双方约定,可能引入隐式依赖。
- 生命周期管理难:需注意内存泄漏和事件粘性。
-
代码示例:
kotlin// 定义事件 data class LoginEvent(val userId: String) // 发布事件 EventBus.getDefault().post(LoginEvent("123")) // 订阅事件 @Subscribe(threadMode = ThreadMode.MAIN) fun onLoginEvent(event: LoginEvent) { /* 处理逻辑 */ }
3. 模块化接口(接口下沉 + 服务发现)
-
原理:
- 接口下沉:将跨模块调用的接口定义在公共基础库。
- 服务发现:通过 SPI(如 ServiceLoader)或 Dagger2 依赖注入获取实现。
-
特点:
- 高内聚低耦合:依赖接口而非具体实现。
- 适合复杂交互:如订单模块调用支付模块的能力。
-
代码示例:
kotlin// 公共模块定义接口 interface IPaymentService { fun pay(orderId: String) } // 支付模块实现接口 class PaymentServiceImpl : IPaymentService { override fun pay(orderId: String) { /* 支付逻辑 */ } } // 订单模块调用(通过 Dagger 注入) @Inject lateinit var paymentService: IPaymentService paymentService.pay("order_123")
4. ContentProvider
-
原理:通过 URI 共享数据,适用于跨进程数据查询。
-
特点:
- 安全可控:通过权限机制限制访问。
- 性能较低:适合低频数据访问(如配置信息共享)。
-
代码示例:
kotlin// 定义 ContentProvider class ConfigProvider : ContentProvider() { override fun query(uri: Uri, ...): Cursor { // 返回配置数据 } } // 其他模块查询 val cursor = contentResolver.query(CONFIG_URI, null, null, null, null)
5. 隐式 Intent
-
原理:通过 Action 或 Category 匹配组件。
-
特点:
- Android 原生支持:无需第三方库。
- 灵活性差:需在 Manifest 中声明 Intent-Filter,维护成本高。
-
代码示例:
kotlin// 跳转到其他模块的 Activity val intent = Intent("com.example.ACTION_VIEW_PROFILE") intent.putExtra("userId", "123") startActivity(intent)
6. 全局单例(谨慎使用)
-
原理:通过全局对象(如 Application)持有数据或服务引用。
-
特点:
- 简单直接:适合小型项目。
- 高耦合性:破坏模块化设计,难维护和测试。
-
代码示例:
kotlin// 全局单例 object AppServiceHub { var userService: UserService? = null } // 模块 A 注册服务 AppServiceHub.userService = UserServiceImpl() // 模块 B 调用服务 AppServiceHub.userService?.getUser("123")
二、方案对比与选型建议
方案 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
路由框架 | 页面跳转、服务调用 | 解耦性强,支持复杂逻辑 | 需引入第三方库 |
事件总线 | 简单状态通知(如登录成功) | 轻量、快速实现 | 难以追踪数据流,可能内存泄漏 |
模块化接口 | 模块间能力调用(如支付、分享) | 高内聚低耦合 | 需设计接口,依赖注入框架 |
ContentProvider | 跨进程数据共享 | 安全可控 | 性能低,适合低频访问 |
隐式 Intent | 简单页面跳转(跨应用) | 无需额外依赖 | 灵活性差,维护成本高 |
全局单例 | 小型项目快速原型 | 实现简单 | 破坏模块化,难扩展 |
三、最佳实践
1. 分层设计:
- 上层业务模块:使用路由框架跳转页面。
- 底层能力模块:通过模块化接口暴露功能(如网络、存储)。
2. 依赖方向:
- 单向依赖(如 App 依赖业务模块,业务模块依赖基础库)。
3. 通信原则:
- 优先选择 接口调用 (显式协议)而非 事件广播(隐式协议)。
- 避免直接依赖其他模块的类或资源。
4. 工具整合:
- 使用 Dagger/Hilt 管理跨模块服务依赖。
- 结合 Gradle 模块化配置,控制代码可见性(如
api
vsimplementation
)。
四、高级方案(大型项目适用)
- APT + 注解处理器:自动生成路由表或服务发现代码。
- 模块化 Gradle 插件:动态控制模块加载和通信。
- Binder 跨进程通信:通过 AIDL 实现高性能 IPC(如独立进程的服务模块)。
通过合理选择通信方式,可以在保证组件独立性的同时,实现灵活高效的模块协作。