Hy
I recently migarted my projekt from dagger 2 to Hilt. Everything went well, but when I modified my WorkManager class, since my worker hasn't done anything.
In logcat I found this error message: WM-WorkerFactory: Could not instantiate hu.crm.crmapp.workmanager.SynchronizationWorker
java.lang.NoSuchMethodException: hu.crm.crmapp.workmanager.SynchronizationWorker. [class android.content.Context, class androidx.work.WorkerParameters]
First of all, I checked all of things, that I found in stackoverflow, so I deleted thw workmanager provider from manifest.
The Sync,and PrefManager dependies I also provided, but I don't copy that bunch of code here.
My Woker class:
#HiltWorker
class SynchronizationWorker #AssistedInject constructor(
private val sync: Sync,
private val prefManager: PrefManager,
#Assisted private val context: Context,
#Assisted workerParams: WorkerParameters
) : Worker(context, workerParams) {
private val countDownLatch = CountDownLatch(1)
override fun doWork(): Result {
val notificationHelper = NotificationHelper(context)
var workResult: Result = Result.success()
//doThings
}
My Application class:
#HiltAndroidApp
class CrmApp : Application(), Configuration.Provider {
#Inject
lateinit var workerFactory: HiltWorkerFactory
#Inject
lateinit var errorLogDao: ErrorLogDao
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
MultiDex.install(this)
}
override fun onCreate() {
super.onCreate()
BuildTypeInitializations.init(this)
}
override fun getWorkManagerConfiguration(): Configuration {
return Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
}
}
And there is the call of Worker class
val constraint =
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED).build()
val synchronizationWorker =
OneTimeWorkRequest.Builder(SynchronizationWorker::class.java)
.setConstraints(constraint)
.setBackoffCriteria(
BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS
)
.build()
WorkManager.getInstance(requireContext()).enqueue(synchronizationWorker)
Thanks for the help.
I encountered the same problem and error when I wanted to inject constructor parameters in the Workmanager with the Dagger-Hilt. Follow these steps to inject constructor parameters in the Workmanager with Hilt:
Step 1: Remove the default initializer from the AndroidManifest.xml:
<application>
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
</application>
(As you've stated, you have already done this part)
Step 2: In your Application class insert this code:
#HiltAndroidApp
class ImageSearchApplication : Application(), Configuration.Provider{
#Inject lateinit var workerFactory: MyWorkerFactory
override fun getWorkManagerConfiguration() =
Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.setWorkerFactory(workerFactory)
.build()
}
Step 3: Now create this class called MyWorkerFactory like this:
class MyWorkerFactory #Inject constructor (private val repository: UnsplashRepository) : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): ListenableWorker? {
// This only handles a single Worker, please don’t do this!!
// See below for a better way using DelegatingWorkerFactory
return MyWorker(appContext, workerParameters, repository)
}
}
That's it! Note that, pass and inject all the parameters you need in the Worker class. I needed only my repository so I defined and injected it.
Related
I'm facing a problem with work manager. I want to pass a dao to work manager constructor and follow all the implementations but can't access the dao.
java.lang.NoSuchMethodException: com.itmedicus.pdmderma.worker.DermaWorker. [class android.content.Context, class androidx.work.WorkerParameters]
Worker Class
#HiltWorker
class DermaWorker #AssistedInject constructor(
#Assisted appContext: Context,
#Assisted params: WorkerParameters,
private val dermaDao: DermaDao
): CoroutineWorker ( appContext,params)
{
override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
try {
DamiData.addDermaList()
val list = DamiData.dermaList
for (derma in list) {
dermaDao.insertDermaContent(derma)
}
Result.success()
} catch (ex: Exception) {
Result.failure()
}
}
}
Application Class
#HiltAndroidApp
class PdmDerma : Application(), Configuration.Provider {
#Inject
lateinit var workerFactory: HiltWorkerFactory
companion object {
lateinit var appContext: Context
}
override fun onCreate() {
super.onCreate()
appContext = applicationContext
plant(Timber.DebugTree())
}
override fun getWorkManagerConfiguration(): Configuration {
return Configuration.Builder().setWorkerFactory(workerFactory)
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.build()
}
}
Manifest
<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" />
</provider>
HiltWorkerModule
#Module
#InstallIn(SingletonComponent::class)
object WorkManagerInitializer : Initializer<WorkManager> {
#Provides
#Singleton
override fun create(#ApplicationContext context: Context): WorkManager {
val configuration = Configuration.Builder().build()
WorkManager.initialize(context, configuration)
return WorkManager.getInstance(context)
}
override fun dependencies(): List<Class<out Initializer<*>>> {
// No dependencies on other libraries.
return emptyList()
}
}
Build.gradle
//Dagger Hilt
implementation 'com.google.dagger:hilt-android:2.42'
kapt 'com.google.dagger:hilt-compiler:2.42'
kapt 'androidx.hilt:hilt-compiler:1.0.0'
implementation 'androidx.hilt:hilt-work:1.0.0'
implementation "androidx.startup:startup-runtime:1.1.1"
// work manager
implementation "androidx.work:work-runtime-ktx:2.7.1"
Where is your factory class? Like this?
#AssistedFactory
interface Factory {
fun create(appContext: Context, params: WorkerParameters, dermaDao: DermaDao): DermaWorker
}
}
Yes.I solved the issue. I'm trying to init workmanager from a viewmodel.In the viewmodel constructor, I'm passing private val workManager : WorkManager.But in this process, I can't init the workmanager.So I'm keeping the AndroidViewModel for context & manually creating the workmanager dependency. Here is my solving code.
#HiltViewModel
class DermaViewModel #Inject constructor(
private val dermaRepository: DermaRepository,
application
) : AndroidViewModel(context) {
init {
initWorkManager()
}
private fun initWorkManager() {
val request: WorkRequest = OneTimeWorkRequestBuilder<DermaWorker>()
.build()
val workManager = WorkManager.getInstance(context)
workManager.enqueue(request)
}
}
I have a Worker which Injects 2 classes, UriUtil and CameraUtil. Both classes are being provided by the AppModule. Even in other classes which do have inheritance with for example FileProvider, or don't inherit any class at all. The vars remain null which, if I'm correct, should be provided by the field injection.
#Module
#InstallIn(SingletonComponent::class)
object AppModule {
#Provides
#Singleton
fun uriUtil(): UriUtil { return UriUtil() }
#Provides
#Singleton
fun cameraUtil(): CameraUtil { return CameraUtil() }
}
My worker then tries to field inject, but they remain null:
#HiltWorker
class SendToBackendWorker #AssistedInject constructor(
#Assisted val context: Context,
#Assisted val workerParams: WorkerParameters
) : Worker(context, workerParams) {
...
#Inject
lateinit var uriUtil: UriUtil
#Inject
lateinit var cameraUtil: CameraUtil
...
}
As the HiltWorker documentation explained (can be found here), I've updated my Application class:
#HiltAndroidApp
class MyApp : Application(), Configuration.Provider {
#Inject
lateinit var workerFactory: HiltWorkerFactory
override fun getWorkManagerConfiguration() =
Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
}
My AndroidManifest contains:
<application
android:name=".MyApp"
...
Can I perhaps only use field injection in classes which inherit from the ones mentioned here?
I've found the solution. There's some additional code required for 'custom' classes, which do not inherit from the ones mentioned here.
For example, to fix the #Inject code in my Worker I had to add an #EntryPoint notation and provide it an interface:
#InstallIn(SingletonComponent::class)
#EntryPoint
interface BackendWorkerEntryPoint {
fun uriUtil(): UriUtil
fun cameraUtil(): CameraUtil
}
private fun hiltEntryPoint(appContext: Context) = EntryPointAccessors.fromApplication(
appContext,
BackendWorkerEntryPoint::class.java
)
private fun getCameraUtil(appContext: Context): CameraUtil =
hiltEntryPoint(appContext).cameraUtil()
private var cameraUtil = getCameraUtil(context)
...
For workManager library version 2.6.0-alpha01 and above you must add this code to your manifest:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="remove" />
read warning in the bottom of this page from developers.android.com
I have initiated a worker in my App class like below;
#HiltAndroidApp
class KutuphanemApplication:Application(),Configuration.Provider {
#Inject
lateinit var workerFactory: HiltWorkerFactory
override fun getWorkManagerConfiguration() =
Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.setWorkerFactory(workerFactory)
.build()
I want to inject a singleton dao into my worker class for update something in DB. So I am injecting dao like below;
#HiltWorker
class ClearImageNotInArchiveWorker #AssistedInject constructor(
#Assisted val appContext: Context,
#Assisted val workerParams: WorkerParameters,
private val kitapDao: KitapDao): CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {...}
}
When I enquee this WorkManager class in App class , I am getting
WorkerFactory: Could not instantiate com.mesutemre.kutuphanem.job.ClearImageNotInArchiveWorker
error. But application is not crashing. Is there any problem about injecting dao in here?
Also my singleton dao;
#Singleton
#Provides
fun provideKitapDao(database: KutuphanemDatabase) = database.getKitapDao();
I solved the problem. I messed up hilt-compiler kapts. I have added
kapt("androidx.hilt:hilt-compiler:1.0.0")
into the gradle then I added
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove">
</provider>
into AndroidManifest.xml . Then the problem solved.
So first of all I created my worker class called UpdaterWorker.
After that I overrided getWorkManagerConfiguration inside my application class.
And I created a WorkerFactory class.
Is that all I have to do, or did I miss something?
How can I create an one time request?
When I try to inject updater worker to main activity, I get this error: Dagger does not support injecting #AssistedInject type, .workmanager.UpdaterWorker. Did you mean to inject its assisted factory type instead?
**Updater worker**
#HiltWorker
class UpdaterWorker #AssistedInject constructor(
private val api: ReservationApi,
private val updaterUrl: String,
private val prefManager: PrefManager,
#Assisted private val context: Context,
#Assisted workerParams: WorkerParameters
) : Worker(context, workerParams) {
...
}
** Worker Factory**
class WorkerFactory #Inject constructor(
private val api: ReservationApi,
#param:Named("update_url") private val updaterUrl: String,
private val prefManager: PrefManager,
) : WorkerFactory() {
override fun createWorker(
appContext: Context,
workerClassName: String,
workerParameters: WorkerParameters
): ListenableWorker? {
return when (workerClassName) {
WorkerEnums.UpdaterWorker.name -> {
UpdaterWorker(api, updaterUrl, prefManager, appContext, workerParameters)
}
else ->
throw Resources.NotFoundException("Worker not found")
}
}
}
** My application class**
override fun getWorkManagerConfiguration(): Configuration {
return Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
}
**Main Activity**
#Inject
lateinit var updaterWorker: UpdaterWorker
A WorkerFactory is not needed and furthermore, you don't need to (and can't) inject the Worker into any other class. Here is the right approach:
Worker
#HiltWorker
class UpdaterWorker #AssistedInject constructor(
private val api: ReservationApi,
private val updaterUrl: String,
private val prefManager: PrefManager,
#Assisted private val context: Context,
#Assisted workerParams: WorkerParameters
) : Worker(context, workerParams) {
...
}
Usage of Worker (e.g here in viewmodel)
class MyTestViewModelForWorker(#ApplicationContext private val context: Context) : ViewModel() {
private val myTestWork = OneTimeWorkRequestBuilder<UpdateWorker>
.build()
fun startManager() {
WorkManager.getInstance(context).enqueue(myTestWork)
}
}
Then, it is important to override the default workerfactory with the hiltfactory in you app class
Overriding default WorkerFactory
#HiltAndroidApp
class App : Application(), Configuration.Provider {
#Inject lateinit var workerFactory: HiltWorkerFactory
override fun getWorkManagerConfiguration(): Configuration =
Configuration.Builder().setWorkerFactory(workerFactory).build()
}
And finally, you need to remove the default workmanagerinitalizator inside your manifest:
AppManifest
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
Next time, please read the documentation of hilt, because everything I wrote here is perfectly explained there and second of all, the approach I am showing here will change with the release of androidx.work:work-runtime-ktx:2.6.0
I'm trying to set up work manager to do some work and I'm having trouble initializing it.
Im using KOIN workmaanger dsl
implementation "org.koin:koin-androidx-workmanager:2.2.0-rc-4"
and my worker class looks like this
class NotificationsScheduler(
private val dispatchers: AppCoroutineDispatchers,
private val getTaskUseCase: GetTaskUseCase,
private val context: Context,
private val workerParameters: WorkerParameters
) : Worker(context, workerParameters) {
override fun doWork(): Result {
...
}
What I've done so far is disabled default initializer
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
tools:node="remove" />
My worker module is set up like this
val workerModule = module {
worker { NotificationsScheduler(get(), get(), get(), get()) }
}
and it is added in list used in startKoin DSL. I've also used workManagerFactory() DSL to set up factory.
startKoin {
androidContext(this#MyApplication)
workManagerFactory()
modules(koinModules)
}
What I'm having trouble with, is that it crashes when app start with exception:
Caused by: org.koin.core.error.NoBeanDefFoundException: No definition found for class:'androidx.work.WorkerParameters'. Check your definitions!
Just take NotificationsScheduler class implements KoinComponent and inject the AppCoroutineDispatchers and GetTaskUseCase instances by inject() like this:
class NotificationsScheduler(context: Context, parameters: WorkerParameters) : CoroutineWorker(context, parameters), KoinComponent {
private val dispatchers: AppCoroutineDispatchers by inject()
private val getTaskUseCase: GetTaskUseCase by inject()
}
In worker module:
val workerModule = module {
worker { OneTimeWorkRequestBuilder<AlarmNotificationHandleWorker>().run{
WorkManager.getInstance(androidContext())
.enqueueUniqueWork(UUID.randomUUID().toString()
,ExistingWorkPolicy.APPEND, this)
}
}
}
Make sure you had provided the GetTaskUseCase and AppCoroutineDispatchers instances
Updated: Koin 2.2.0 release:
implementation "org.koin:koin-androidx-workmanager:2.2.0"
Update your Worker class
class NotificationsScheduler(private val dispatchers: AppCoroutineDispatchers,private val getTaskUseCase: GetTaskUseCase,context: Context, parameters: WorkerParameters) : CoroutineWorker(context, parameters), KoinComponent {
}
And here you are:
val workerModule = module {
worker { NotificationsScheduler(get(),get(),androidContext(),get()) }
}
Thanks #p72b