10. Hilt与WorkManager:@HiltWorker、Worker中依赖注入的实现
后台任务,说白了就是App里最容易被忽视的“隐形战场”。
WorkManager是Android官方推荐的后台任务调度库,但如果你不用依赖注入,Worker类里写一堆手动new出来的依赖,那代码很快就会变成一团乱麻。我个人习惯是:能用Hilt注入的,绝不手动创建。
10.1 为什么Worker需要依赖注入?
你想想看,Worker的生命周期是由WorkManager管理的,不是由我们控制的。每次任务执行时,系统会通过WorkerFactory来创建Worker实例。如果我们想往Worker里传Repository、ApiService这些依赖,怎么办?
传统做法是在Worker的构造方法里手动获取:
class UploadWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
private val repository = (applicationContext as MyApp).appContainer.repository
// ...
}
嗯,这里要注意——这种写法有几个问题:
- 强依赖Application,测试时很难替换
- 耦合严重,换一个容器实现就得改代码
- 可读性差,依赖关系不透明
我在项目中遇到过类似情况,当时一个同事把所有依赖都写在Worker里,结果单元测试写了三天还没跑通。后来改成Hilt注入,半小时就搞定了。
10.2 @HiltWorker 注解
Hilt从1.0版本开始就支持Worker的依赖注入。核心就是@HiltWorker这个注解。
用法很简单:
@HiltWorker
class UploadWorker @AssistedInject constructor(
@Assisted context: Context,
@Assisted workerParams: WorkerParameters,
private val repository: UploadRepository,
private val apiService: ApiService
) : Worker(context, workerParams) {
override fun doWork(): Result {
return try {
val data = repository.getPendingUploads()
apiService.upload(data)
Result.success()
} catch (e: Exception) {
Result.retry()
}
}
}
这里有几个关键点:
- @HiltWorker:标记这个Worker需要Hilt注入
- @AssistedInject:因为Context和WorkerParameters是WorkManager传进来的,不是我们提供的,所以要用辅助注入
- @Assisted:标记那些由系统提供的参数
- 其他参数:repository、apiService这些,Hilt会自动注入
重要提醒:@HiltWorker只能用于直接继承Worker的类。如果你用了CoroutineWorker或者ListenableWorker,需要额外配置。
10.3 配置HiltWorkerFactory
光加注解还不够,你得告诉WorkManager用Hilt的工厂来创建Worker。这一步很多人会忘。
在Application类里配置:
@HiltAndroidApp
class MyApplication : Application(), Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory
override fun getWorkManagerConfiguration(): Configuration {
return Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
}
}
为什么需要这一步?
因为WorkManager默认使用自己的WorkerFactory,它不认识Hilt的注解。我们通过Configuration.Provider接口,把HiltWorkerFactory传进去,这样WorkManager创建Worker时就会走Hilt的流程。
小技巧:如果你用了Hilt的@HiltAndroidApp,HiltWorkerFactory会自动生成,不需要自己写实现类。直接@Inject就行。
10.4 CoroutineWorker 的注入
现在很多项目都用协程,CoroutineWorker比普通Worker更灵活。注入方式略有不同:
@HiltWorker
class SyncWorker @AssistedInject constructor(
@Assisted context: Context,
@Assisted workerParams: WorkerParameters,
private val syncRepository: SyncRepository
) : CoroutineWorker(context, workerParams) {
override suspend fun doWork(): Result {
return withContext(Dispatchers.IO) {
try {
syncRepository.syncAll()
Result.success()
} catch (e: Exception) {
if (runAttemptCount < 3) {
Result.retry()
} else {
Result.failure()
}
}
}
}
}
注意,CoroutineWorker的doWork()是挂起函数,里面可以直接用协程。依赖注入的方式和普通Worker完全一样。
10.5 多模块项目中的Worker注入
如果你的项目分了多个模块,比如:data、:domain、:worker,那Worker通常放在单独的模块里。这时候要注意Hilt的模块可见性。
举个例子:
// :worker 模块
@HiltWorker
class CleanupWorker @AssistedInject constructor(
@Assisted context: Context,
@Assisted workerParams: WorkerParameters,
private val cleanupRepository: CleanupRepository // 来自 :data 模块
) : Worker(context, workerParams) { ... }
只要CleanupRepository在:data模块里用@Singleton或@ViewModelScoped等注解提供了,Hilt就能跨模块注入。不需要额外配置。
我曾经踩过的坑:如果Worker模块依赖了其他模块,但那个模块的Hilt组件没有正确安装(比如忘了加@InstallIn),Hilt会在编译时报错。检查一下每个模块的Module有没有正确配置。
10.6 测试Worker的依赖注入
测试是依赖注入的一大优势。用Hilt注入的Worker,测试时只需要替换依赖即可:
@HiltAndroidTest
class UploadWorkerTest {
@get:Rule
val hiltRule = HiltAndroidRule(this)
@Inject
lateinit var workerFactory: HiltWorkerFactory
@Mock
lateinit var mockRepository: UploadRepository
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
// 替换真实依赖为Mock
// 这里可以用Hilt的测试模块来替换
}
@Test
fun testUploadSuccess() {
val context = ApplicationProvider.getApplicationContext<Context>()
val worker = TestWorkerBuilder
.from(context, UploadWorker::class.java, workerFactory)
.build()
val result = worker.doWork()
assertEquals(Result.success(), result)
}
}
用TestWorkerBuilder配合workerFactory,就能创建出注入好依赖的Worker实例。测试起来非常方便。
10.7 知识体系图
10.8 常见问题与避坑
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 编译报错:@HiltWorker只能用于Worker子类 | 用了CoroutineWorker但没加@HiltWorker | 确认类上加了@HiltWorker注解 |
| 运行时Worker创建失败 | 没有配置HiltWorkerFactory | 在Application中实现Configuration.Provider |
| 依赖注入为空 | @AssistedInject和@Inject混用 | 统一用@AssistedInject + @Assisted |
| 多模块下找不到依赖 | 模块的Hilt Module没有正确安装 | 检查@InstallIn注解和组件范围 |
个人建议:如果你刚开始用Hilt+WorkManager,先从一个简单的Worker开始,跑通整个流程。确认HiltWorkerFactory配置正确后,再逐步添加复杂依赖。我曾经一次性写了五个Worker,结果排查问题花了一整天。
好了,这一章的内容就到这里。Hilt和WorkManager的结合,说白了就是让后台任务也能享受依赖注入带来的便利。配置一次,后面写Worker就轻松多了。
公众号:蓝海资料掘金营,微信deep3321