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

从这篇文章开始,我将记录在实战中进行的架构优化/升级方面的经验。目前我所承接的是一个 维护了7年,包含10余个复杂模块 的庞大 APP,有无数祖传代码累积。而我现在要做的,就是要为这一辆行驶中的小汽车更换掉老旧的轮胎,使它能够更长远、更稳定地运行下去。
这一系列的首篇文章,先从接入 Hilt 框架开始。
项目现状分析
这是一个运行在 Android 平台,配合智能穿戴终端使用的项目。该项目用于在手机上与智能穿戴设备进行配对、连接、设置等。其场景覆盖全面,复杂度高,是一个极好的研究对象:
- 多种数据格式和传输协议:蓝牙协议、HTTPS、对称、非对称加密等。
- 软硬件结合:结合智能穿戴设备传感器所采集到的各种数据,配对连接多种硬件,正向、反向兼容性需求高。
- 深入系统:该项目涉及形形色色的系统 API 调用,同时,该项目所采集到的数据也会对外开放使用。
- 大数据量处理 :由于智能穿戴设备采集到的数据是
7*24
小时的,存在海量数据在设备之间同步的场景。面临着多端上传数据不一致的问题。
这个项目规模庞大、场景复杂、迭代历史悠久、架构劣化严重,因此,我希望可以通过有意识地在架构方面进行升级优化,降低项目的维护成本,提升代码运行效率,更快地定位问题、开发新需求。
目标
首先明确架构升级要达到的目标:
- 分层明确:各层级之间单向依赖,有明确的作用域和语义区分。
- 高内聚低耦合:同一份逻辑不允许出现2次,以复用为设计目标。
- 利于单元测试和集成测试:可以在开发时对某一数据源进行灵活替换,以便进行数据 mock、单元测试等。
- 避免过度设计:平衡开发成本和收益。
具体实现
先从分层次入手,首先尝试引入 Hilt 作为依赖注入(DI)框架,代替手动初始化组件的原始方式。
Hilt 的优点与使用场景
- 依赖解耦:不需要手动创建依赖对象,避免类之间强耦合。
- 生命周期感知 :Hilt 可以把对象绑定到 Android 组件的生命周期(
SingletonComponent
、ActivityRetainedComponent
、ViewModelComponent
等),Repository
是全局单例,UseCase
绑定到Activity
,ViewModel
绑定到ViewModel
生命周期。这样避免了资源泄漏或对象复用不当的问题。 - 可测试性:可以灵活地替换为 Fake 或者 Mock 实现。
- 可读性高:Hilt 的依赖图清晰,接手项目的工程师很容易理解"这个对象从哪来"。
个人理解是,对于有明确方向性的业务依赖场景(如 Repository
、UseCase
、网络客户端、数据库 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
}
}