当前位置: 首页 > news >正文

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博客 

http://www.lryc.cn/news/587780.html

相关文章:

  • iOS如何查看电池容量?理解系统限制与开发者级能耗调试方法
  • H.264编解码(NAL)
  • 前端docx库实现将html页面导出word
  • 蜻蜓I即时通讯水银版系统直播功能模块二次开发文档-详细的直播功能模块文档范例-卓伊凡|麻子
  • 文档处理控件Aspose.Words教程:从 C# 中的 Word 文档中提取页面
  • 【飞牛云fnOS】告别数据孤岛:飞牛云fnOS私人资料管家
  • Python爬虫实战:研究PyMongo库相关技术
  • crawl4ai--bitcointalk爬虫实战项目
  • 嵌入式硬件篇---ne555定时器
  • 嵌入式硬件篇---晶体管的分类
  • Android 中 实现格式化字符串
  • Selenium动态网页爬虫编写与解释
  • 【Linux】Jenkins Lts 配置构建 Maven 项目
  • C++之哈希表的基本介绍以及其自我实现(开放定址法版本)
  • Maven 依赖原则和依赖冲突
  • 【Spring AI Alibaba】接入大模型
  • openGL学习(EBO)
  • Spring 学习笔记
  • 2025/7/14——java学习总结
  • JavaSE-8-多态
  • 机械硬盘文件丢失为何大概率能恢复
  • JavaScript中Object.defineProperty的作用和用法以及和proxy的区别
  • Linux多进程
  • 《美术教育研究》是什么级别的期刊?是正规期刊吗?能评职称吗?
  • Combine的介绍与使用
  • C++-linux 7.文件IO(三)文件元数据与 C 标准库文件操作
  • SVD、DCT图像压缩实践
  • 什么是电磁锁控制板?24路锁控板的使用步骤概述
  • MySQL数据库的基础操作
  • Java Integer包装类缓存机制详解