基于Dagger-Hilt的CoroutineWorker实例化失败(NoSuchMethodException)排查
问题:WorkManager无法实例化NotificationsWorker(NoSuchMethodException)
基于Jetpack Compose和Dagger-Hilt开发Android应用,需实现每日周期性通知功能,通知标题根据当前日期从Room数据库获取。但Worker执行任务时,Logcat抛出java.lang.NoSuchMethodException,无法实例化NotificationsWorker。已确认POST_NOTIFICATIONS权限正常,应用目标API为33。
错误日志
Could not instantiate com.example.myapp.ui.notifications.NotificationsWorker java.lang.NoSuchMethodException: com.example.myapp.ui.notifications.NotificationsWorker.<init> [class android.content.Context, class androidx.work.WorkerParameters] at java.lang.Class.getConstructor0(Class.java:2363) at java.lang.Class.getDeclaredConstructor(Class.java:2201) at androidx.work.WorkerFactory.createWorkerWithDefaultFallback(WorkerFactory.java:95) at androidx.work.impl.WorkerWrapper.runWorker(WorkerWrapper.java:243) at androidx.work.impl.WorkerWrapper.run(WorkerWrapper.java:145) at androidx.work.impl.utils.SerialExecutorImpl$Task.run(SerialExecutorImpl.java:96) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637) at java.lang.Thread.run(Thread.java:1012) Could not create Worker com.example.myapp.ui.notifications.NotificationsWorker
相关代码
NotificationsWorker.kt
class NotificationsWorker( context: Context, workerParameters: WorkerParameters, private val repository: MyRepository ): CoroutineWorker(context, workerParameters) { override suspend fun doWork(): Result { val intent = Intent(applicationContext, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } val pendingIntent: PendingIntent = PendingIntent.getActivity( applicationContext, 0, intent, PendingIntent.FLAG_IMMUTABLE ) val today = LocalDate.now() var dailyItem = "" repository.getDailyItem(today).collect { dailyItem = it.title } val builder = NotificationCompat.Builder(applicationContext, "myChannelId") .setSmallIcon(R.drawable.icon) .setContentTitle(dailyItem) .setPriority(NotificationCompat.PRIORITY_HIGH) .setContentIntent(pendingIntent) with(NotificationManagerCompat.from(applicationContext)) { if (ActivityCompat.checkSelfPermission( applicationContext, Manifest.permission.POST_NOTIFICATIONS ) != PackageManager.PERMISSION_GRANTED ) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { ActivityCompat.requestPermissions( MainActivity(), arrayOf(Manifest.permission.POST_NOTIFICATIONS), 1 ) } } notify(1, builder.build()) } return Result.success() } }
NotificationsScheduler.kt
class NotificationsScheduler @Inject constructor(@ApplicationContext private val context: Context) { fun scheduleNotification(hourOfDay: Int, minute: Int) { val now = Calendar.getInstance() val target = Calendar.getInstance().apply { set(Calendar.HOUR_OF_DAY, hourOfDay) set(Calendar.MINUTE, minute) } val timeDiff = target.timeInMillis - now.timeInMillis val notificationRequest = PeriodicWorkRequestBuilder<NotificationsWorker>(15, TimeUnit.MINUTES) .addTag("daily_item") .build() WorkManager.getInstance(context) .enqueueUniquePeriodicWork( "myapp_notification_work", ExistingPeriodicWorkPolicy.UPDATE, notificationRequest ) } }
解决方案
1. 核心问题定位
WorkManager默认通过反射实例化Worker,要求Worker必须提供仅接收Context和WorkerParameters两个参数的构造函数。当前NotificationsWorker额外添加了MyRepository参数,导致反射找不到符合要求的构造函数,抛出NoSuchMethodException。
2. 用Hilt正确注入Worker依赖
要在Worker中使用Hilt注入依赖,需遵循以下步骤:
步骤1:修改NotificationsWorker
添加@HiltWorker注解,并用@Assisted标注构造函数的Context和WorkerParameters参数:
import dagger.hilt.android.workers.HiltWorker import dagger.assisted.Assisted import dagger.assisted.AssistedInject import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import android.content.Context @HiltWorker class NotificationsWorker @AssistedInject constructor( @Assisted context: Context, @Assisted workerParameters: WorkerParameters, private val repository: MyRepository ) : CoroutineWorker(context, workerParameters) { override suspend fun doWork(): Result { val intent = Intent(applicationContext, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } val pendingIntent: PendingIntent = PendingIntent.getActivity( applicationContext, 0, intent, PendingIntent.FLAG_IMMUTABLE ) val today = LocalDate.now() // 改用firstOrNull()避免Flow无限制挂起 val dailyItem = repository.getDailyItem(today).firstOrNull()?.title ?: "今日无内容" val builder = NotificationCompat.Builder(applicationContext, "myChannelId") .setSmallIcon(R.drawable.icon) .setContentTitle(dailyItem) .setPriority(NotificationCompat.PRIORITY_HIGH) .setContentIntent(pendingIntent) with(NotificationManagerCompat.from(applicationContext)) { // 已确认权限正常,移除错误的权限请求逻辑 notify(1, builder.build()) } return Result.success() } }
步骤2:创建HiltWorkerFactory
让Hilt管理Worker的实例化:
import androidx.work.ListenableWorker import androidx.work.WorkerFactory import androidx.work.WorkerParameters import android.content.Context import javax.inject.Inject import javax.inject.Provider import javax.inject.Singleton @Singleton class HiltWorkerFactory @Inject constructor( private val workerFactories: Map<Class<out ListenableWorker>, @JvmSuppressWildcards Provider<ChildWorkerFactory>> ) : WorkerFactory() { override fun createWorker( appContext: Context, workerClassName: String, workerParameters: WorkerParameters ): ListenableWorker? { val workerClass = Class.forName(workerClassName).asSubclass(ListenableWorker::class.java) return workerFactories[workerClass]?.get()?.create(appContext, workerParameters) } } interface ChildWorkerFactory { fun create(appContext: Context, params: WorkerParameters): ListenableWorker }
步骤3:在Application中配置WorkManager
指定WorkManager使用HiltWorkerFactory:
import android.app.Application import androidx.work.Configuration import dagger.hilt.android.HiltAndroidApp import javax.inject.Inject @HiltAndroidApp class MyApp : Application(), Configuration.Provider { @Inject lateinit var workerFactory: HiltWorkerFactory override fun getWorkManagerConfiguration(): Configuration { return Configuration.Builder() .setWorkerFactory(workerFactory) .build() } }
3. 额外修复点
- 移除Worker中
ActivityCompat.requestPermissions(MainActivity(), ...):Worker是后台组件,不能直接实例化Activity来请求权限,且已确认权限正常,无需保留该逻辑。 - 将
repository.getDailyItem(today).collect改为firstOrNull():Room返回的Flow若为单条数据,collect会一直挂起直到Flow关闭,改用firstOrNull()可获取数据后立即返回,避免不必要的挂起。
内容的提问来源于stack exchange,提问作者zkvvoob
相关产品推荐
相关产品推荐

