Android模块化架构:基于依赖注入和服务定位器的解耦方案
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>): Tfun <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(): Booleanfun 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): Booleanfun login(username: String, password: String): Boolean // 应该属于LoginServicefun 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 技术亮点
- 服务定位器模式:统一的服务管理和获取机制
- 依赖注入:运行时依赖注入,避免编译时耦合
- 接口隔离:模块间通过接口通信,实现真正的解耦
- 模块自治:每个模块独立管理自己的服务实现
这个架构方案在保持简洁性的同时,实现了真正的模块解耦,是一个值得分享的优秀实践!
后面我会分享出来项目架构代码。
但是也存在一个问题: 我跳转页面都是固定页面,属于硬编码,如果封装成库,就有考虑动态路由。
先链接:
Android 自定义路由系统-CSDN博客
将该依赖注入+服务定位器做成库 的分析和具体实现
手写路由库 (上)(类似ARouter或VMRouter)-CSDN博客
手写路由库 (下)(类似ARouter或VMRouter)-CSDN博客
ServiceLibrary 库使用演示-CSDN博客