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

相关推荐
fox_mt12 分钟前
AI Coding - ClaudeCode使用指南
java·ai编程
min18112345615 分钟前
PC端零基础跨职能流程图制作教程
大数据·人工智能·信息可视化·架构·流程图
毕设源码-郭学长30 分钟前
【开题答辩全过程】以 基于SSM的高校运动会管理系统的设计与实现为例,包含答辩的问题和答案
java·eclipse
qq_54702617932 分钟前
Maven 使用指南
java·maven
静听松涛13333 分钟前
中文PC端多人协作泳道图制作平台
大数据·论文阅读·人工智能·搜索引擎·架构·流程图·软件工程
xiaolyuh12340 分钟前
Arthas修改类(如加日志)的实现原理
java
栗子叶44 分钟前
Java对象创建的过程
java·开发语言·jvm
stevenzqzq1 小时前
android启动初始化和注入理解3
android
有一个好名字1 小时前
力扣-从字符串中移除星号
java·算法·leetcode
zfj3211 小时前
CyclicBarrier、CountDownLatch、Semaphore 各自的作用和用法区别
java·开发语言·countdownlatch·semaphore·cyclicbarrier