A plain Worker blocks a background thread in doWork(). A CoroutineWorker makes doWork() a suspend function on Dispatchers.Default, so it can call BookNest's repository directly, and stopping the work cancels the coroutine. Since WorkManager 2.9 234 it lives in work-runtime; work-runtime-ktx is empty.
WorkManager builds workers from a Context and WorkerParameters alone. androidx.hilt:hilt-work (1.4.0, with ksp on androidx.hilt:hilt-compiler) generates a factory per @HiltWorker; those two arguments are @Assisted, the rest come from Hilt:
@HiltWorker
class CatalogSyncWorker @AssistedInject constructor(
@Assisted context: Context, // supplied by WorkManager
@Assisted params: WorkerParameters,
private val repo: BookRepository, // supplied by Hilt
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
...
val before = cachedBooks() // prices before the refresh
return when {
repo.refresh() -> Result.success(workDataOf(KEY_BOOKS to afterSync(before)))
runAttemptCount < 4 -> Result.retry() // back off, then run again
else -> Result.failure()
}
}
...
}afterSync() returns the book count (Notifications and Widgets adds notifications and a widget). success() may carry output Data, retry() applies the backoff, and failure() stops, failing the rest of a chain.
BookNestApp then implements Configuration.Provider: its workManagerConfiguration property (the Hilt guide still shows the old getWorkManagerConfiguration()) passes an injected HiltWorkerFactory to Configuration.Builder().setWorkerFactory(). The manifest removes WorkManager's default initializer (tools:node="remove" on the WorkManagerInitializer meta-data), which would start WorkManager with the default factory. SyncReportWorker has no @HiltWorker, so WorkManager builds it by reflection.