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)

Kotlin 复制代码
// 服务提供者接口

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 依赖注入机制

Kotlin 复制代码
// 基础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)

Kotlin 复制代码
// 用户服务接口

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 服务实现层 (各业务模块)

Kotlin 复制代码
// 用户服务实现 (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 模块注册机制

Kotlin 复制代码
// 用户模块注册 (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 应用初始化

Kotlin 复制代码
class App : Application() {

    override fun onCreate() {

        super.onCreate()

        

        // 初始化各模块服务

        initModules()

    }

    

    private fun initModules() {

        // 注册各模块服务

        LoginModule().initialize()

        UserModule().initialize()

        // 其他模块初始化...

    }

}

5. 使用示例

5.1 在Activity中使用服务

Kotlin 复制代码
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 服务间依赖

Kotlin 复制代码
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 服务设计原则

Kotlin 复制代码
// ✅ 好的设计:接口职责单一

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 模块划分原则

Kotlin 复制代码
 

// ✅ 按业务功能划分

mod_login/     // 登录相关

mod_user/      // 用户相关  

mod_home/      // 首页相关

mod_common/    // 公共接口

// ✅ 按技术功能划分

lib_net/       // 网络库

lib_basic/     // 基础库

lib_mqtt/      // MQTT库

8.3 错误处理

Kotlin 复制代码
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博客

相关推荐
Decay__4 小时前
05_MySQL 常用 SQL 大全 —— 诊断、管理、优化辅助 SQL 与三大系统库速查
运维·数据库·sql·mysql·架构
代码山河6 小时前
Python就业方向全解析:后端、爬虫、数据分析、AI你选哪个?
python·学习·架构·教程·面向对象·项目
学长毕设6 小时前
基于SpringBoot的实验室资产管理系统
java·spring boot·后端
驰骋工作流7 小时前
流程时效考核的表结构:从待办时限到 WF_CH 考核结果驰骋BPM驰骋低代码低代码工作流引擎
android·低代码·rxjava
微订校园外卖平台8 小时前
校园外卖平台选模板还是定制:架构、数据流与验收清单
大数据·架构·外卖系统·本地生活·微订
zzu123zsw8 小时前
从臃肿的模板调用到声明式契约:深入 UniHttp 动态代理与多渠道生命周期扩展
java·spring boot·microsoft·unihttp
源码集结号8 小时前
云诊所门诊系统源码,基于SpringBoot+Vue.js架构,支持多租户设计与Docker容器化部署
java·spring boot·源码·医院信息系统·门诊系统·诊所系统
传奇开心果编程9 小时前
【Flutter踩坑排雷】第7课:巨型 build 函数坑|全部 UI 写在同一个 build,维护难、隐性异常
android·flutter·ios·移动开发·跨平台·dart·声明式ui
论文复现现场9 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
ZhengEnCi9 小时前
用 AI 写代码为什么拼到最后还是拼人:算力瓶颈与领域专家提示词的本质差异
算法·架构