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

相关推荐
ST.J1 小时前
swing笔记
java·笔记
菩提树下的凡夫1 小时前
瑞芯微RV1126目标识别算法Yolov8的部署应用
java·算法·yolo
爱隐身的官人1 小时前
新后端漏洞(上)- Java RMI Registry反序列化漏洞
java·反序列化漏洞
叫我阿柒啊1 小时前
从Java全栈到前端框架:一次真实的面试对话与技术解析
java·javascript·typescript·vue·springboot·react·前端开发
晚安里1 小时前
Spring 框架(IoC、AOP、Spring Boot) 的必会知识点汇总
java·spring boot·spring
爱隐身的官人1 小时前
新后端漏洞(上)- Aapache Tomcat AJP 文件包含漏洞(CVE-2020-1938)
java·tomcat·ajp
@CLoudbays_Martin112 小时前
为什么动态视频业务内容不可以被CDN静态缓存?
java·运维·服务器·javascript·网络·python·php
四谎真好看2 小时前
Java 学习笔记(进阶篇2)
java·笔记·学习
码界奇点2 小时前
KingbaseES一体化架构与多层防护体系如何保障企业级数据库的持续稳定与弹性扩展
数据库·架构·可用性测试
上官浩仁2 小时前
springboot ioc 控制反转入门与实战
java·spring boot·spring