Android模块化架构:基于依赖注入和服务定位器的解耦方案

发布于:2025-07-17 ⋅ 阅读:(16) ⋅ 点赞:(0)

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

// 基础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(): Boolean

    fun 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): Boolean

    fun login(username: String, password: String): Boolean  // 应该属于LoginService

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


网站公告

今日签到

点亮在社区的每一天
去签到