HiltWorker 使用附加参数抛出 NoSuchMethodException

问题描述 投票:0回答:1

我的 2 个 CoroutineWorkers 出现以下异常,并带有由 Hilt 注入的附加参数。对于没有附加参数的工作人员来说,这种情况不会发生,只有当我通过我的 fastlane 构建通过 APK 安装应用程序时才会发生这种情况。 Android Studio 构建工作完美。但我的构建管道使用与 Android Studio 构建相同的设置。通过工作,我的意思是工作人员排队并且不会抛出以下异常...

Could not instantiate com.example.android.ActivitySyncWorker
    java.lang.NoSuchMethodException: 
       com.example.android.ActivitySyncWorker.<init> [class android.content.Context, class androidx.work.WorkerParameters]
       at java.lang.Class.getConstructor0(Class.java:3325)
       at java.lang.Class.getDeclaredConstructor(Class.java:3063)
       at androidx.work.WorkerFactory.createWorkerWithDefaultFallback(WorkerFactory.java:94)
       at androidx.work.impl.WorkerWrapper.runWorker(WorkerWrapper.java:243)
       at androidx.work.impl.WorkerWrapper.run(WorkerWrapper.java:144)
       at androidx.work.impl.utils.SerialExecutorImpl$Task.run(SerialExecutorImpl.java:96)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
       at java.lang.Thread.run(Thread.java:1012)

Hilt

mWorkerFactories
的内部变量为空。那时我的工人就找不到了。但对于 Android Studio 构建来说,它并不是空的。 在我继续之前。这个问题与其他类似问题的不同之处在于,我有一个多模块项目。也许我因此而错过了一些东西。

这是应用程序模块内的 Worker

:core-ble

@HiltWorker
class ActivitySyncWorker @AssistedInject constructor(
    @Assisted context: Context,
    @Assisted params: WorkerParameters,
    val bleServiceModule: BleServiceModule,
    private val connectionPool: BleConnectionPool,
) : CoroutineWorker(context, params) {
    override suspend fun doWork(): Result {
        return coroutineScope {
            try {
                // ...

                Result.success()
            } catch (exception: Exception) {
                SLog.e("[BLE_SYNC] worker error: $exception")
                if (runAttemptCount < 2) {
                    Result.retry()
                } else {
                    Result.failure()
                }
            }
        }
    }

    companion object {
        private const val ACTIVITY_SYNC_WORKER_TAG = "activity_sync"

        fun enqueue(context: Context, workManager: WorkManager, workerParams: Data = Data.EMPTY) {
            val workRequest = PeriodicWorkRequestBuilder<ActivitySyncWorker>(30, TimeUnit.MINUTES).apply {
                addTag(ACTIVITY_SYNC_WORKER_TAG)
                setConstraints(
                    Constraints.Builder()
                        .setRequiredNetworkType(NetworkType.CONNECTED)
                        .build()
                )
                setInputData(
                    Data.Builder()
                        .putAll(workerParams)
                        .build()
                )
            }.build()

            workManager.enqueueUniquePeriodicWork(
                /* uniqueWorkName = */ ACTIVITY_SYNC_WORKER_TAG,
                /* existingPeriodicWorkPolicy = */ ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
                /* periodicWork = */ workRequest
            )
        }

        fun stop(context: Context) {
            WorkManager.getInstance(context).cancelAllWorkByTag(ACTIVITY_SYNC_WORKER_TAG)
        }
    }
}

该工作线程始终排队,但没有额外的注入参数。因为反射,所以可以找到。

@HiltWorker
class RemoteConfigSyncWorker @AssistedInject constructor(
    @Assisted context: Context,
    @Assisted params: WorkerParameters,
) : CoroutineWorker(context, params) {
    override suspend fun doWork(): Result {
        return coroutineScope {
            try {
                // ...

                Result.success()
            } catch (exception: Exception) {
                if (runAttemptCount < 2) {
                    Result.retry()
                } else {
                    Result.failure()
                }
            }
        }
    }

    companion object {
        private const val REMOTE_CONFIG_SYNC_WORKER_TAG = "remote_config_sync"

        fun enqueue(workManager: WorkManager, workerParams: Data = Data.EMPTY) {
            val workRequest = PeriodicWorkRequestBuilder<RemoteConfigSyncWorker>(30, TimeUnit.MINUTES).apply {
                addTag(REMOTE_CONFIG_SYNC_WORKER_TAG)
                setConstraints(
                    Constraints.Builder()
                        .setRequiredNetworkType(NetworkType.CONNECTED)
                        .build()
                )
                setInputData(
                    Data.Builder()
                        .putAll(workerParams)
                        .build()
                )
            }.build()

            workManager.enqueueUniquePeriodicWork(
                /* uniqueWorkName = */ REMOTE_CONFIG_SYNC_WORKER_TAG,
                /* existingPeriodicWorkPolicy = */ ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
                /* periodicWork = */ workRequest
            )
        }

        fun stop(context: Context) {
            WorkManager.getInstance(context).cancelAllWorkByTag(REMOTE_CONFIG_SYNC_WORKER_TAG)
        }
    }
}

正如您在我的清单中看到的,我删除了默认的初始化程序并添加了我自己的......

        <provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
            tools:node="merge">
            <meta-data
                android:name="androidx.work.WorkManagerInitializer"
                android:value="androidx.startup"
                tools:node="remove" />
            <meta-data
                android:name="com.example.android.WorkManagerModule"
                android:value="androidx.startup" />
        </provider>

这是新的初始化程序...

@Module
@InstallIn(SingletonComponent::class)
object WorkManagerModule : Initializer<WorkManager> {

    private var isInitialized = false

    @Provides
    @Singleton
    override fun create(@ApplicationContext context: Context): WorkManager {
        if (!isInitialized) { // just in case this gets called twice
            val entryPoint = EntryPointAccessors.fromApplication(
                context,
                HiltWorkerFactoryEntryPoint::class.java
            )

            val configuration = Configuration.Builder()
                .setWorkerFactory(entryPoint.workerFactory())
                .setMinimumLoggingLevel(Log.VERBOSE)
                .build()

            WorkManager.initialize(context, configuration)

            isInitialized = true
        }

        return WorkManager.getInstance(context)
    }

    override fun dependencies(): MutableList<Class<out Initializer<*>>> {
        return mutableListOf()
    }

    @EntryPoint
    @InstallIn(SingletonComponent::class)
    interface HiltWorkerFactoryEntryPoint {
        fun workerFactory(): HiltWorkerFactory
    }
}

我在位于

:app

的 MultiDexApplication 的 onCreate 中执行此操作
AppInitializer.getInstance(this).initializeComponent(WorkManagerModule::class.java)

我的进口...(在

:core-ble
:app
中。

 implementation(libs.hilt)
 implementation(libs.hilt.work)
 implementation(libs.hilt.plugin)
 kapt(libs.hilt.compiler)
 kapt(libs.hilt.androidx.compiler)
 implementation(libs.work.runtime.ktx)
 implementation(libs.startup)
hilt = "2.44.2"
hiltAndroidX = "1.1.0"
hiltCompose = "1.0.0"
hiltWork = "1.0.0"
work = "2.9.0"
hiltKapt = "2.35"

hilt-plugin = { group = "com.google.dagger", name = "hilt-android-gradle-plugin", version.ref = "hilt" }
hilt-kapt = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hiltKapt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" }
hilt-androidx-compiler = { group = "androidx.hilt", name = "hilt-compiler", version.ref = "hiltAndroidX" }
hilt-work = { group = "androidx.hilt", name = "hilt-work", version.ref = "hiltWork" }
startup = { group = "androidx.startup", name = "startup-runtime", version.ref = "startup" }
hilt = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
work-runtime = { group = "androidx.work", name = "work-runtime", version.ref = "work" }
work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "work" }

我也尝试添加这些 ProGuard 规则,但为什么它应该适用于 Android Studio 构建而不适用于 APK 构建。

-keepclassmembers class * extends androidx.work.Worker {
    public <init>(android.content.Context,androidx.work.WorkerParameters);
}

-keepclassmembers class * extends androidx.work.CoroutineWorker {
    public <init>(android.content.Context,androidx.work.WorkerParameters);
}

这是我的 WorkManager 参数之一的柄模块。里面的参数也一样

provideBleServiceModule

@Module
@InstallIn(SingletonComponent::class)
object BleServiceModuleModule {

    @Singleton
    @Provides
    fun provideBleServiceModule(
        @ApplicationContext context: Context,
        sharedPrefHelper: CommonsSharedPrefHelper,
        bluetoothAdapterStateModule: BluetoothAdapterStateModule,
        userDbService: UserDbService,
        bleEvents: BleEvents,
    ): BleServiceModule = BleServiceModule(
        context,
        sharedPrefHelper,
        bluetoothAdapterStateModule,
        userDbService,
        bleEvents,
    )
}
android kotlin dagger-hilt android-workmanager
1个回答
0
投票

我最终编写了自己的 WorkerFactory 并在那里注入了必要的参数......

class BleWorkerFactory @Inject constructor(
    private val bleServiceModule: BleServiceModule,
) : WorkerFactory() {
    override fun createWorker(appContext: Context, workerClassName: String, workerParameters: WorkerParameters): ListenableWorker? {
        return when (workerClassName) {
            RemoteConfigSyncWorker::class.java.name -> RemoteConfigSyncWorker(appContext, workerParameters)
            HardwareReportWorker::class.java.name -> HardwareReportWorker(appContext, workerParameters, bleServiceModule)
            ActivitySyncWorker::class.java.name -> ActivitySyncWorker(appContext, workerParameters, bleServiceModule)
            else -> null
        }
    }
}
@Singleton
class MyDelegatingWorkerFactory @Inject constructor(
    bleWorkerFactory: BleWorkerFactory
) : DelegatingWorkerFactory() {
    init {
        addFactory(bleWorkerFactory)
    }
}

在我的应用程序中,我没有实现 Configuration.Provider,因为在覆盖 workManagerConfiguration val 时我得到了

RuntimeException: Unable to instantiate application
。相反,我在 onCreate() 中设置配置。

@HiltAndroidApp
open class RandomHiltApplication : MyApplication() {

    @EntryPoint
    @InstallIn(SingletonComponent::class)
    interface WorkerFactoryEntryPoint {
        fun workerFactory(): MyDelegatingWorkerFactory
    }

    override fun onCreate() {
        val workManagerConfiguration: Configuration = Configuration.Builder()
            .setWorkerFactory(EntryPoints.get(this, WorkerFactoryEntryPoint::class.java).workerFactory())
            .setMinimumLoggingLevel(Log.VERBOSE)
            .build()
        WorkManager.initialize(this, workManagerConfiguration)
        super.onCreate()
    }
}

我的 Fastlane 构建现在正在运行,我对此感到满意。遗憾的是,我找不到 HiltWorkerFactory 不起作用的原因。

© www.soinside.com 2019 - 2024. All rights reserved.