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

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐