1. 项目背景与挑战
1.1 项目特点
- 多应用构建:支持多个独立应用(robot_demo、mqttcontrolcommand等)
- 模块化架构:按业务功能划分模块(mod_login、mod_user、mod_home等)
- 组件化开发:基础功能封装为库(lib_basic、lib_net、lib_mqtt等)
1.2 面临挑战
- 模块间依赖复杂:如何避免模块间直接依赖?
- 服务共享困难:如何在不同模块间共享服务?
- 多应用适配:如何让同一套代码支持多个应用?
- 编译时耦合:如何实现真正的编译时解耦?
2. 架构设计理念
2.1 核心思想
接口定义 → 服务实现 → 依赖注入 → 模块解耦
2.2 设计原则
- 接口隔离:模块间通过接口通信,不依赖具体实现
- 服务定位:通过服务定位器统一管理服务实例
- 依赖注入:运行时注入依赖,避免编译时耦合
- 模块自治:每个模块独立管理自己的服务实现
3. 核心架构组件
3.1 服务定位器模式 (Service Locator Pattern)
// 服务提供者接口
interface ServiceProvider {
fun <T> get(serviceClass: Class<T>): T
fun <T> register(serviceClass: Class<T>, implementation: T)
}
// 服务定位器实现
class ServiceLocator : ServiceProvider {
private val services = mutableMapOf<Class<*>, Any>()
override fun <T> register(serviceClass: Class<T>, implementation: T) {
services[serviceClass] = implementation as Any
}
override fun <T> get(serviceClass: Class<T>): T {
return services[serviceClass] as T
}
}
// 全局服务定位器
object GlobalServiceLocator {
private val serviceLocator = ServiceLocator()
fun <T> register(serviceClass: Class<T>, implementation: T) {
serviceLocator.register(serviceClass, implementation)
}
fun <T> get(serviceClass: Class<T>): T {
return serviceLocator.get(serviceClass)
}
}
3.2 依赖注入机制
// 基础Activity提供服务获取能力
abstract class BaseMvvmActivity<DB : ViewBinding, VM : ViewModel> : BaseDataBindActivity<DB>() {
// 强制获取服务(必需服务)
protected fun <T> getService(serviceClass: Class<T>): T {
return GlobalServiceLocator.get(serviceClass)
?: throw IllegalStateException("Service ${serviceClass.simpleName} not found")
}
// 安全获取服务(可选服务)
protected fun <T> getServiceOrNull(serviceClass: Class<T>): T? {
return GlobalServiceLocator.get(serviceClass)
}
}
4. 模块化实现方案
4.1 接口定义层 (mod_common)
// 用户服务接口
interface UserService {
fun toUserPage(context: Context)
fun getUserInfo(userId: String): AccountInfo?
suspend fun updateUserInfo(username: String, password: String): BaseResponse<Boolean>
}
// 登录服务接口
interface LoginService {
suspend fun login(username: String, password: String): BaseResponse<User>
fun isLoggedIn(): Boolean
fun logout()
fun getCurrentUser(): User?
}
4.2 服务实现层 (各业务模块)
// 用户服务实现 (mod_user)
class UserServiceImpl : UserService {
private val networkService = GlobalServiceLocator.get(NetworkService::class.java)
override fun toUserPage(context: Context) {
UserActivity.start(context)
}
override fun getUserInfo(userId: String): AccountInfo? {
val localAccount = AccountManager.getAccount()
return if(localAccount?.getName().equals(userId)){
localAccount
} else {
null
}
}
override suspend fun updateUserInfo(username: String, password: String): BaseResponse<Boolean> {
return networkService.request {
apiUserInterface.updateAccount(username, password)
}
}
}
// 登录服务实现 (mod_login)
class LoginServiceImpl : LoginService {
private val networkService = GlobalServiceLocator.get(NetworkService::class.java)
override suspend fun login(username: String, password: String): BaseResponse<User> {
return networkService.request {
apiLoginInterface.login(LoginRequest(username, password, "", ""))
}
}
}
4.3 模块注册机制
// 用户模块注册 (mod_user)
class UserModule {
fun initialize() {
GlobalServiceLocator.register(UserService::class.java, UserServiceImpl())
}
}
// 登录模块注册 (mod_login)
class LoginModule {
fun initialize() {
GlobalServiceLocator.register(LoginService::class.java, LoginServiceImpl())
}
}
4.4 应用初始化
class App : Application() {
override fun onCreate() {
super.onCreate()
// 初始化各模块服务
initModules()
}
private fun initModules() {
// 注册各模块服务
LoginModule().initialize()
UserModule().initialize()
// 其他模块初始化...
}
}
5. 使用示例
5.1 在Activity中使用服务
class LoginActivity : BaseMvvmActivity<ActivityLoginBinding, LoginViewModel>() {
// 通过DI获取服务
private val userService: UserService by lazy {
getService(UserService::class.java)
}
override fun initView(savedInstanceState: Bundle?) {
// 测试依赖注入解耦
val accountInfo = try {
userService.getUserInfo("testUserId")
} catch (e: Exception) {
Log.e("LoginActivity", "Error fetching user info: ${e.message}")
null
}
mBinding.etPhone.setText(accountInfo?.getName() ?: "")
}
private fun initListener() {
mBinding.tvRegister.onClick {
// 通过服务接口进行页面跳转,完全解耦
userService.toUserPage(this)
}
}
}
5.2 服务间依赖
class UserServiceImpl : UserService {
// 通过服务定位器获取其他服务
private val networkService = GlobalServiceLocator.get(NetworkService::class.java)
private val loginService = GlobalServiceLocator.get(LoginService::class.java)
override fun toUserPage(context: Context) {
// 可以结合其他服务进行业务逻辑处理
if (loginService.isLoggedIn()) {
UserActivity.start(context)
} else {
LoginActivity.start(context)
}
}
}
6. 架构优势分析
6.1 解耦性
- 编译时解耦:模块间通过接口通信,不依赖具体实现
- 运行时绑定:服务实例在运行时确定,支持动态替换
- 依赖倒置:高层模块不依赖低层模块,都依赖抽象
6.2 可维护性
- 职责清晰:每个模块负责自己的业务逻辑
- 接口稳定:接口变更影响范围可控
- 易于测试:可以轻松替换服务实现进行单元测试
6.3 可扩展性
- 模块独立:新模块可以独立开发和部署
- 服务可插拔:服务实现可以动态替换
- 多应用支持:同一套代码支持多个应用构建
6.4 开发体验
- 类型安全:编译时检查服务类型
- IDE支持:良好的代码提示和重构支持
- 渐进式迁移:可以逐步迁移现有代码
7. 与主流方案对比
7.1 相比传统依赖注入框架 (Dagger/Hilt)
特性 | 服务定位器模式 | Dagger/Hilt |
---|---|---|
学习成本 | 低 | 高 |
编译时检查 | 部分 | 完整 |
运行时性能 | 较好 | 优秀 |
多模块支持 | 优秀 | 复杂 |
调试难度 | 简单 | 复杂 |
7.2 相比路由框架 (ARouter)
特性 | 服务定位器模式 | ARouter |
---|---|---|
业务逻辑处理 | 优秀 | 有限 |
类型安全 | 优秀 | 一般 |
页面跳转 | 通过服务 | 直接路由 |
模块解耦 | 优秀 | 良好 |
8. 最佳实践
8.1 服务设计原则
// ✅ 好的设计:接口职责单一
interface UserService {
fun getUserInfo(userId: String): AccountInfo?
fun updateUserInfo(user: User): Boolean
}
// ❌ 避免:接口职责过多
interface UserService {
fun getUserInfo(userId: String): AccountInfo?
fun updateUserInfo(user: User): Boolean
fun login(username: String, password: String): Boolean // 应该属于LoginService
fun sendNotification(message: String): Boolean // 应该属于NotificationService
}
8.2 模块划分原则
// ✅ 按业务功能划分
mod_login/ // 登录相关
mod_user/ // 用户相关
mod_home/ // 首页相关
mod_common/ // 公共接口
// ✅ 按技术功能划分
lib_net/ // 网络库
lib_basic/ // 基础库
lib_mqtt/ // MQTT库
8.3 错误处理
class LoginActivity {
private val userService: UserService by lazy {
getService(UserService::class.java)
}
private fun handleUserInfo() {
try {
val userInfo = userService.getUserInfo("userId")
// 处理用户信息
} catch (e: Exception) {
// 优雅降级
Log.e("LoginActivity", "Failed to get user info", e)
showDefaultUserInfo()
}
}
}
9. 总结
9.1 架构价值
- 真正的模块解耦:通过接口和服务定位器实现编译时解耦
- 灵活的服务管理:支持运行时服务替换和动态配置
- 优秀的多应用支持:同一套代码支持多个应用构建
- 良好的开发体验:类型安全、IDE支持、易于调试
9.2 适用场景
- 大型模块化项目:需要严格模块隔离的项目
- 多应用构建:需要支持多个应用的项目
- 团队协作开发:需要明确模块边界和接口契约
- 渐进式重构:需要逐步解耦现有代码
9.3 技术亮点
- 服务定位器模式:统一的服务管理和获取机制
- 依赖注入:运行时依赖注入,避免编译时耦合
- 接口隔离:模块间通过接口通信,实现真正的解耦
- 模块自治:每个模块独立管理自己的服务实现
这个架构方案在保持简洁性的同时,实现了真正的模块解耦,是一个值得分享的优秀实践!
后面我会分享出来项目架构代码。
但是也存在一个问题: 我跳转页面都是固定页面,属于硬编码,如果封装成库,就有考虑动态路由。
先链接:
将该依赖注入+服务定位器做成库 的分析和具体实现
手写路由库 (上)(类似ARouter或VMRouter)-CSDN博客