【业务场景架构实战】1. 多模块 Hilt 使用原则和环境搭建

凡傲之凌物,不必定以言语加人,有以神气凌之者矣,有以面色凌之者矣。

从这篇文章开始,我将记录在实战中进行的架构优化/升级方面的经验。目前我所承接的是一个 维护了7年,包含10余个复杂模块 的庞大 APP,有无数祖传代码累积。而我现在要做的,就是要为这一辆行驶中的小汽车更换掉老旧的轮胎,使它能够更长远、更稳定地运行下去。

这一系列的首篇文章,先从接入 Hilt 框架开始。

项目现状分析

这是一个运行在 Android 平台,配合智能穿戴终端使用的项目。该项目用于在手机上与智能穿戴设备进行配对、连接、设置等。其场景覆盖全面,复杂度高,是一个极好的研究对象:

  • 多种数据格式和传输协议:蓝牙协议、HTTPS、对称、非对称加密等。
  • 软硬件结合:结合智能穿戴设备传感器所采集到的各种数据,配对连接多种硬件,正向、反向兼容性需求高。
  • 深入系统:该项目涉及形形色色的系统 API 调用,同时,该项目所采集到的数据也会对外开放使用。
  • 大数据量处理 :由于智能穿戴设备采集到的数据是 7*24 小时的,存在海量数据在设备之间同步的场景。面临着多端上传数据不一致的问题。

这个项目规模庞大、场景复杂、迭代历史悠久、架构劣化严重,因此,我希望可以通过有意识地在架构方面进行升级优化,降低项目的维护成本,提升代码运行效率,更快地定位问题、开发新需求。

目标

首先明确架构升级要达到的目标:

  • 分层明确:各层级之间单向依赖,有明确的作用域和语义区分。
  • 高内聚低耦合:同一份逻辑不允许出现2次,以复用为设计目标。
  • 利于单元测试和集成测试:可以在开发时对某一数据源进行灵活替换,以便进行数据 mock、单元测试等。
  • 避免过度设计:平衡开发成本和收益。

具体实现

先从分层次入手,首先尝试引入 Hilt 作为依赖注入(DI)框架,代替手动初始化组件的原始方式。

Hilt 的优点与使用场景

  • 依赖解耦:不需要手动创建依赖对象,避免类之间强耦合。
  • 生命周期感知 :Hilt 可以把对象绑定到 Android 组件的生命周期(SingletonComponentActivityRetainedComponentViewModelComponent 等),Repository 是全局单例,UseCase 绑定到 ActivityViewModel 绑定到 ViewModel 生命周期。这样避免了资源泄漏或对象复用不当的问题。
  • 可测试性:可以灵活地替换为 Fake 或者 Mock 实现。
  • 可读性高:Hilt 的依赖图清晰,接手项目的工程师很容易理解"这个对象从哪来"。

个人理解是,对于有明确方向性的业务依赖场景(如 RepositoryUseCase、网络客户端、数据库 DAO 等),建议使用 Hilt。对于简单的静态工具场景,可以用 object 单例快速实现。

搭建 Hilt 依赖

首先来看本项目的架构缩略图:

这是一个多模块(module)的项目,其主模块是 main-app,它是一个空壳模块,仅用于完成 Application 的初始化。其具体业务逻辑拆分为不同的 business-module,通过 settings.gradle 集成在一起。

因此,在阅读完 Hilt 使用文档后,我了解到需要进行以下两方面的接入:

module 引入 plugin 引入 dependency 声明 kapt
main-app
business-wear 🚫

必须在应用层(app 模块)的 build.gradle 引入 Hilt 插件,因为:

  • @HiltAndroidApp 只能写在 Application 类 上,而 Application 只能存在于 app 模块。
  • Hilt 的 Gradle 插件会为 Application 生成 Hilt_*Application,所以 必须在 app 模块应用插件。

而为了使用 Hilt/Dagger 注解处理器,则必须在两个模块里声明 kapt

main-app/build.gradle

gradle 复制代码
plugins {
    id 'com.google.dagger.hilt.android'
}

dependencies {
    implementation project(":business-wear")
    implementation "com.google.dagger:hilt-android:2.x.x"
    kapt "com.google.dagger:hilt-android-compiler:2.x.x"
}

business-wear/build.gradle

gradle 复制代码
dependencies {
    implementation "com.google.dagger:hilt-android:2.x.x"
    kapt "com.google.dagger:hilt-android-compiler:2.x.x"
}

另外需要注意,Hilt 和 ARouter 兼容性不佳,需要在 app 的配置里增加以下代码:

gradle 复制代码
hilt {
    enableAggregatingTask = false
}

实现 Repository-ViewModel-Activity 代码

gradle 文件里完成上述配置后,代码里按照以下方式接入 Hilt:

1. 在 Application 中初始化

如果 Application 存在继承关系,要把注解加在 AndroidManifest.xml 中用到的那个 Application 类上面。

kotlin 复制代码
@HiltAndroidApp
class MyApp : Application()

2. 创建 Repository

Repository 类本身无需注解,对其构造函数进行 @Inject

用单例 onject RepositoryModule 创建一个工厂,并将其安装为进程内单例唯一 SingletonComponent::class

kotlin 复制代码
// Repository 用普通类
class UserRepository @Inject constructor() {
    fun getUserName(): String {
        return "Alice"
    }
}

// Hilt Module(声明作用域)
@Module
@InstallIn(SingletonComponent::class)
object RepositoryModule {
    @Provides
    fun provideUserRepository(): UserRepository {
        return UserRepository()
    }
}

3. ViewModel 注入 Repository

使用 @HiltViewModel 注解 ViewModel,并在构造函数里进行 @Inject

kotlin 复制代码
@HiltViewModel
class UserViewModel @Inject constructor(
    private val userRepository: UserRepository
) : ViewModel() {

    fun loadUser(): String {
        return userRepository.getUserName()
    }
}

4. 在 Activity 中使用 ViewModel

最后,在终端页面 Activity 上,用 @AndroidEntryPoint 进行注入。

kotlin 复制代码
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    private val viewModel: UserViewModel by viewModels() // ===> 由 Hilt 管理

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        println(viewModel.loadUser()) // 输出 Alice
    }
}

参考资料

相关推荐
木木的木云11 小时前
从零构建微前端框架:PavilionMfe 设计揭秘
前端·架构·vite
AI-好学者11 小时前
MCP企业运用全面知识点-基础篇
服务器·开发语言·网络·人工智能·python·架构
ai生成式引擎优化技术12 小时前
WSaiOS:面向认知资产与工程化认知流程的智能操作系统架构
python·架构·django·virtualenv·pygame
大侠锅锅12 小时前
第 1 篇:开篇|物联网边缘计算的真实挑战与云边端架构全景
物联网·架构·边缘计算
国科安芯20 小时前
ASC4T245S分组双向控制架构深度解析:独立DIR/OE控制、QFN16封装与混合方向总线桥接
单片机·嵌入式硬件·物联网·fpga开发·架构·risc-v
派叔21 小时前
老字号营销服务商技术解构:三类方案的架构逻辑与选型评估
大数据·人工智能·搜索引擎·架构·产品运营·流量运营
宠友信息1 天前
多端数据互通场景下Spring Boot仿小红书源码结构设计
数据库·spring boot·redis·缓存·架构
这是个栗子1 天前
【前端性能优化】优化数据加载:用 Promise.all 从串行到并行
前端·javascript·性能优化·异步编程·前端优化·promise.all
韩楚风1 天前
【参天引擎】Cantian 服务端框架全景解析:进程架构、模块组成与交互关系
数据库·mysql·架构·cantian
Sinclair1 天前
认识安企CMS-核心功能亮点
架构·开源